Version in base suite: 2.3.1-1 Base version: ausweisapp2_2.3.1-1 Target version: ausweisapp2_2.4.0-2~bpo13+1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/a/ausweisapp2/ausweisapp2_2.3.1-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/a/ausweisapp2/ausweisapp2_2.4.0-2~bpo13+1.dsc .codespellrc | 3 .github/dependabot.yml | 2 .github/workflows/CI_build.yml | 155 - .github/workflows/CI_build_combined.yml | 282 +- .github/workflows/codeql.yml | 19 .gitlab-ci-child.yml | 797 ++++++ .gitlab-ci.yml | 68 .qmlformat.ini | 7 .qmllint.ini | 2 .typos.toml | 188 + .yamlfmt | 7 .yamllint | 6 CMakeLists.txt | 37 CMakePresets.json | 14 CONTRIBUTING.rst | 2 Dockerfile | 2 Dockerfile.vnc | 60 Doxyfile.in | 50 LICENSE.officially.txt | 613 +---- LICENSE.txt | 611 +---- README.rst | 6 appveyor.yml | 109 ci.cmake | 203 + ci/check_formatting.sh | 88 ci/cmake/Android.cmake | 131 + ci/cmake/Bootstrap.cmake | 34 ci/cmake/Configuration.cmake | 1 ci/cmake/Container.cmake | 103 ci/cmake/Deploy.cmake/AAR.cmake | 40 ci/cmake/Deploy.cmake/APK.cmake | 9 ci/cmake/Deploy.cmake/Allowlist.cmake | 10 ci/cmake/Deploy.cmake/Archive.cmake | 40 ci/cmake/Deploy.cmake/Cloud.cmake | 11 ci/cmake/Deploy.cmake/Container.cmake | 10 ci/cmake/Deploy.cmake/Framework.cmake | 37 ci/cmake/Deploy.cmake/GitHub.cmake | 70 ci/cmake/Deploy.cmake/IPA.cmake | 7 ci/cmake/Deploy.cmake/PKG.cmake | 6 ci/cmake/Deploy.cmake/Utils.cmake | 42 ci/cmake/Docker.cmake | 1 ci/cmake/Docs.cmake | 22 ci/cmake/Files.cmake | 162 + ci/cmake/Formatting.cmake | 20 ci/cmake/FreeBSD.cmake | 9 ci/cmake/Libraries.cmake | 118 + ci/cmake/Light.cmake/Analyze.cmake | 1 ci/cmake/Light.cmake/Configuration.cmake | 8 ci/cmake/Light.cmake/Packages.cmake | 1 ci/cmake/Linux.cmake | 28 ci/cmake/MacOS.cmake | 59 ci/cmake/Provider.cmake | 11 ci/cmake/SonarQube.cmake | 133 + ci/cmake/Source.cmake | 11 ci/cmake/Translations.cmake | 6 ci/cmake/Win.cmake | 51 ci/cmake/Win64.cmake | 1 ci/cmake/iOS.cmake | 56 ci/import.py | 177 + ci/pipeline.py | 428 +++ ci/playstore.py | 126 + ci/presets/android.json | 46 ci/presets/bsd.json | 12 ci/presets/ci.json | 26 ci/presets/iOS.json | 40 ci/presets/linux.json | 27 ci/presets/macOS.json | 33 ci/presets/tools.json | 32 ci/presets/windows.json | 32 ci/requirements.txt | 2 ci/sonar-project.properties.in | 24 cmake/Appcast.cmake | 46 cmake/CompilerFlags.cmake | 61 cmake/DVCS.cmake | 24 cmake/FindPCSC.cmake | 9 cmake/Helper.cmake | 24 cmake/Install.cmake | 16 cmake/Libraries.cmake | 51 cmake/Merge.cmake | 2 cmake/Messages.cmake | 1 cmake/Notarization.cmake | 12 cmake/Packaging.android.cmake | 15 cmake/Packaging.cmake | 90 cmake/Pandoc.cmake | 101 cmake/PrepareExecutable.cmake.in | 13 cmake/PrepareProxy.cmake.in | 13 cmake/Provider.cmake | 24 cmake/SignFiles.cmake.in | 2 cmake/Sphinx.cmake | 7 cmake/SwiftPackage.cmake | 33 cmake/Tools.Libraries.cmake | 6 cmake/Tools.cmake | 111 cmake/Translation.cmake.in | 10 cmake/Version.cmake | 2 cmake/android.toolchain.cmake | 9 cmake/ci/Android.cmake | 53 cmake/ci/Configuration.cmake | 4 cmake/ci/Container.cmake | 66 cmake/ci/Docker.cmake | 1 cmake/ci/Docs.cmake | 20 cmake/ci/Formatting.cmake | 3 cmake/ci/FreeBSD.cmake | 5 cmake/ci/Linux.cmake | 19 cmake/ci/MacOS.cmake | 56 cmake/ci/SonarQube.cmake | 49 cmake/ci/Source.cmake | 9 cmake/ci/Win.cmake | 39 cmake/ci/iOS.cmake | 54 cmake/cmd.cmake | 234 +- cmake/iOS.bundles.cmake.in | 27 cmake/macOS.pkg.cmake | 4 cmake/pandoc/convert_tables.lua | 155 + cmake/pandoc/custom_filter.lua | 131 + cmake/pandoc/preprocess_rst.py | 866 +++++++ cmake/pandoc/template.tex | 221 + cmake/prepare_sonarqube_env.cmake | 75 debian/changelog | 33 debian/control | 4 docs/CMakeLists.txt | 32 docs/failurecodes/failurecodes.rst | 4 docs/failurecodes/locales/de/LC_MESSAGES/failurecodes.po | 3 docs/failurecodes/metadata.yaml.in | 10 docs/failurecodes/metadata_de.yaml.in | 10 docs/installation/CommunicationModel_de.graphml | 228 - docs/installation/CommunicationModel_de.pdf | 463 --- docs/installation/CommunicationModel_en.graphml | 228 - docs/installation/CommunicationModel_en.pdf | 466 ---- docs/installation/README.de.rst | 494 ---- docs/installation/README.en.rst | 455 --- docs/installation/conf.py.in | 180 - docs/installation/index.rst | 12 docs/installation_de/CommunicationModel_de.graphml | 228 + docs/installation_de/CommunicationModel_de.pdf | 463 +++ docs/installation_de/README.de.rst | 489 ++++ docs/installation_de/conf.py.in | 180 + docs/installation_de/index.rst | 7 docs/installation_de/metadata.yaml.in | 10 docs/installation_en/CommunicationModel_en.graphml | 228 + docs/installation_en/CommunicationModel_en.pdf | 466 ++++ docs/installation_en/README.en.rst | 450 +++ docs/installation_en/conf.py.in | 180 + docs/installation_en/index.rst | 7 docs/installation_en/metadata.yaml.in | 10 docs/releasenotes/2.3.2.rst | 25 docs/releasenotes/2.4.0.rst | 23 docs/releasenotes/announce.rst | 7 docs/releasenotes/appcast.rst | 10 docs/releasenotes/conf.py.in | 3 docs/releasenotes/issues.rst | 30 docs/releasenotes/metadata.yaml.in | 10 docs/releasenotes/singlehtml.conf.py.in | 111 docs/releasenotes/support.rst | 28 docs/releasenotes/text.conf.py.in | 2 docs/releasenotes/versions.rst | 9 docs/sdk/android.rst | 31 docs/sdk/changelog.rst | 8 docs/sdk/commands.rst | 2 docs/sdk/container.rst | 2 docs/sdk/desktop.rst | 4 docs/sdk/messages.rst | 11 docs/sdk/metadata.yaml.in | 10 docs/sdk/simulator.rst | 7 libs/CMakeLists.txt | 235 +- libs/CMakePresets.json | 15 libs/GovernikusConfig.cmake.in | 16 libs/README.rst | 2 libs/Versions.cmake | 31 libs/patch.cmake | 68 libs/patch.cmake.in | 53 libs/patch.py | 2 libs/patches.cmake | 66 libs/patches/openssl-0001-android-shlib_variant.patch | 2 libs/patches/qtbase-0001-Android-Fix-logging-of-the-category.patch | 136 - libs/patches/qtbase-0001-Do-not-override-OPENSSL_API_COMPAT.patch | 25 libs/patches/qtbase-0002-Do-not-override-OPENSSL_API_COMPAT.patch | 25 libs/patches/qtbase-0002-Don-t-ignore-offscreen-A11y-elements-for-VoiceOver-o.patch | 26 libs/patches/qtbase-0003-Android-Unify-behavior-of-Accessible.description-wit.patch | 45 libs/patches/qtbase-0003-Ask-to-scroll-to-on-focus-changes-in-iOS-VoiceOver.patch | 252 -- libs/patches/qtbase-0004-Revert-Android-Detect-mouse-buttons.patch | 167 - libs/patches/qtbase-0004-iOS-Add-dynamic-locking-of-screen-orientation.patch | 83 libs/patches/qtbase-0005-Android-Set-name-matched-class-names-for-Slider-and-.patch | 41 libs/patches/qtbase-0005-QThread-Unix-do-clean-up-the-QAdoptedThread-for-the-.patch | 161 - libs/patches/qtbase-0006-Add-iOS-UIScene-related-URL-handling.patch | 115 libs/patches/qtbase-0006-a11y-Windows-Implement-UIA_AriaRolePropertyId-for-Ac.patch | 34 libs/patches/qtbase-0007-CMake-Android-add-property-QT_ANDROID_COMPILE_SDK_VE.patch | 168 - libs/patches/qtbase-0007-a11y-iOS-Announce-changes-to-name-description-to-Voi.patch | 83 libs/patches/qtbase-0008-Add-_qt_internal_detect_latest_android_platform.patch | 80 libs/patches/qtbase-0008-a11y-Android-Announce-changes-to-name-description-to.patch | 149 + libs/patches/qtbase-0009-Android-Use-the-TalkBack-focus-signal-to-trigger-foc.patch | 102 libs/patches/qtbase-0009-Android-use-latest-platform-only-if-the-default-is-n.patch | 64 libs/patches/qtbase-0010-Android-Fix-freeze-on-start-when-the-activity-was-de.patch | 121 - libs/patches/qtbase-0010-iOS-Handle-Z-gesture-to-request-closing.patch | 37 libs/patches/qtbase-0011-Check-for-valid-QAccessibleInterface-before-invoking.patch | 131 - libs/patches/qtbase-0011-a11y-Windows-Announce-change-to-Accessible.name-for-.patch | 37 libs/patches/qtbase-0012-Fix-broadcasts-with-dual-stack-IPv6.patch | 30 libs/patches/qtbase-0012-Fix-usage-of-API-functions-on-Windows-1607-Build-143.patch | 101 libs/patches/qtbase-0013-a11y-iOS-Inform-VoiceOver-about-used-language.patch | 40 libs/patches/qtbase-0014-a11y-Android-Inform-TalkBack-about-used-language.patch | 124 + libs/patches/qtbase-0015-iOS-Fix-a11y-representation-of-RadioButton-and-Check.patch | 58 libs/patches/qtbase-0016-a11y-Add-Switch-role.patch | 255 ++ libs/patches/qtbase-0017-Add-QAccessibleScrollInterface-to-inform-Android-Tal.patch | 268 ++ libs/patches/qtbase-0018-Introduce-signal-ScrollingPositionChanged-to-signal-.patch | 68 libs/patches/qtbase-0019-a11y-macOS-Implement-accessibilityMinValue-and-acces.patch | 48 libs/patches/qtbase-0020-a11y-Android-Set-a-RangeInfo-by-using-the-ValueInter.patch | 98 libs/patches/qtbase-0021-a11y-Android-Use-correct-event-type-for-value-update.patch | 40 libs/patches/qtbase-0022-a11y-iOS-Do-not-set-UIAccessibilityTraitAdjustable-f.patch | 37 libs/patches/qtbase-0023-Android-Fix-double-emission-of-certain-a11y-events.patch | 83 libs/patches/qtbase-0024-Android-Avoid-announcing-simple-text-as-clickable.patch | 41 libs/patches/qtbase-0025-Revert-Android-Keyboard-Use-new-show-hide-methods-fo.patch | 172 + libs/patches/qtbase-0026-a11y-Android-Enable-granular-text-navigation-for-edi.patch | 281 ++ libs/patches/qtbase-0027-Revert-iOS-Pause-display-link-when-app-moves-out-of-.patch | 48 libs/patches/qtbase-0028-a11y-iOS-Enable-granular-text-navigation-for-editabl.patch | 259 ++ libs/patches/qtbase-0029-Revert-Re-land-Android-destroy-the-window-surface-on.patch | 218 + libs/patches/qtbase-0030-Pass-through-iOS-focusing-events-to-QMacAccessibilit.patch | 57 libs/patches/qtbase-0031-Replicate-focusability-of-a11y-elements-for-UIFocus.patch | 34 libs/patches/qtbase-0032-Make-nonvisible-vertical-a11y-elements-in-Flickable-.patch | 57 libs/patches/qtbase-0033-Android-a11y-Support-text-selection.patch | 556 ++++ libs/patches/qtbase-0034-Fix-native-a11y-role-of-QAccessible-LayeredPane.patch | 43 libs/patches/qtconnectivity-0001-Increase-NFC-timeout-on-Android.patch | 22 libs/patches/qtconnectivity-0002-iOS-Improve-reporting-of-NFCReaderTransceiveErrorSes.patch | 123 - libs/patches/qtdeclarative-0001-A11y-Send-a11y-events-on-Accessible.ignored-changes.patch | 29 libs/patches/qtdeclarative-0001-Basic-Style-Add-support-for-dark-mode-color-scheme.patch | 198 + libs/patches/qtdeclarative-0002-Implement-minimum-maximum-value-also-for-to-from-pro.patch | 190 + libs/patches/qtdeclarative-0002-Use-MAP_JIT-on-all-Apple-platforms.patch | 46 libs/patches/qtdeclarative-0003-Quick-Remove-revision-from-Accessible.id.patch | 32 libs/patches/qtdeclarative-0003-a11y-Enable-the-ValueInterface-for-the-ProgressBar.patch | 31 libs/patches/qtdeclarative-0004-Implement-QAccessibleScrollInterface-in-QAccessibleQ.patch | 146 + libs/patches/qtdeclarative-0004-Revert-Layouts-Add-revision-to-useDefaultSizePolicy.patch | 41 libs/patches/qtdeclarative-0005-Use-new-signal-QAccessible-ScrollingPositionChanged-.patch | 43 libs/patches/qtdeclarative-0006-Consider-new-Switch-role-introduced-in-qtbase.patch | 87 libs/patches/qtdeclarative-0007-a11y-Consider-modal-QML-Popup-for-a11y-hierarchy.patch | 149 + libs/patches/qtdeclarative-0008-QQuickTextEdit-a11y-Allow-modifying-text-selection-v.patch | 200 + libs/patches/qtdeclarative-0009-QQuickTextInput-a11y-Allow-modifying-text-selection-.patch | 170 + libs/patches/qtdeclarative-0010-A11y-Prepend-new-items-in-ItemView-based-on-modelInd.patch | 120 + libs/patches/qtdeclarative-0011-Remove-special-treatment-for-embedded-hyperlinks.patch | 101 libs/patches/qtdeclarative-0012-A11y-Implement-QAccessibleEditableTextInterface-for-.patch | 145 + libs/patches/qtscxml-0001-Fix-features-and-add-some-new-features.patch | 270 -- libs/patches/qtsvg-0001-Replace-check-for-endless-recursion-when-loading.patch | 104 libs/patches/qtsvg-0002-Don-t-create-group-nodes-which-will-be-deleted-anywa.patch | 147 + libs/patches/qttools-0001-Fix-build-with-no-feature-mdiarea-and-no-feature-fon.patch | 238 -- libs/patches/qttranslations-0001-a11y-iOS-Provide-translations-for-the-new-Rotor-name.patch | 42 libs/qt-install.qs | 98 libs/qt.cmake | 95 libs/test/valgrind.supp | 53 libs/test/valgrind.supp.DEBUG | 32 libs/test/valgrind.supp.RELEASE | 32 libs/test/valgrind.supp.RELWITHDEBINFO | 36 presets/ci-android.json | 44 presets/ci-bsd.json | 12 presets/ci-iOS.json | 40 presets/ci-linux.json | 25 presets/ci-macOS.json | 33 presets/ci-tools.json | 33 presets/ci-windows.json | 32 presets/ci.json | 26 resources/CMakeLists.txt | 28 resources/ausweisapp.qrc | 2 resources/config.json.in | 12 resources/jenkins/check_formatting.sh | 85 resources/jenkins/docker/Dockerfile | 60 resources/jenkins/docker/alpine-common/Dockerfile | 13 resources/jenkins/docker/alpine-docs/Dockerfile | 2 resources/jenkins/docker/alpine-linux/Dockerfile | 4 resources/jenkins/docker/alpine-swarm/Dockerfile | 4 resources/jenkins/docker/generate.py | 28 resources/jenkins/docker/ubuntu-android/Dockerfile | 10 resources/jenkins/docker/ubuntu-swarm/Dockerfile | 10 resources/jenkins/docker/ubuntu-vanilla/Dockerfile | 1 resources/jenkins/dsl/Builds/Build_Android.groovy | 8 resources/jenkins/dsl/Builds/Build_Appcast.groovy | 3 resources/jenkins/dsl/Builds/Build_SDK.groovy | 12 resources/jenkins/dsl/Builds/Build_SonarQube.groovy | 5 resources/jenkins/dsl/Builds/Build_Win64_GNU_MSI.groovy | 2 resources/jenkins/dsl/Builds/Build_Win64_MSVC_MSI.groovy | 2 resources/jenkins/dsl/Builds/Build_Win64_MSVC_MSI_dev.groovy | 2 resources/jenkins/dsl/Builds/Build_Win64_clang_MSI.groovy | 4 resources/jenkins/dsl/Builds/Build_iOS_Framework.groovy | 19 resources/jenkins/dsl/Builds/Build_iOS_Framework_OS.groovy | 19 resources/jenkins/dsl/Builds/Build_iOS_Framework_Simulator.groovy | 19 resources/jenkins/dsl/Builds/Build_iOS_Framework_Simulator_x86_64.groovy | 19 resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy | 2 resources/jenkins/dsl/Builds/Build_iOS_SwiftPackage.groovy | 12 resources/jenkins/dsl/Libraries/Libs_Android.groovy | 2 resources/jenkins/dsl/Libraries/Libs_iOS.groovy | 20 resources/jenkins/dsl/Libraries/Libs_iOS_OS.groovy | 20 resources/jenkins/dsl/Libraries/Libs_iOS_Simulator.groovy | 20 resources/jenkins/dsl/Libraries/Libs_iOS_Simulator_x86_64.groovy | 20 resources/jenkins/dsl/Releases/Release_Android.groovy | 10 resources/jenkins/dsl/Releases/Release_Appcast.groovy | 21 resources/jenkins/dsl/Releases/Release_Container.groovy | 5 resources/jenkins/dsl/Releases/Release_Win64_GNU.groovy | 2 resources/jenkins/dsl/Releases/Release_Win64_MSVC.groovy | 2 resources/jenkins/dsl/Releases/Release_iOS.groovy | 2 resources/jenkins/dsl/Releases/Release_iOS_Framework.groovy | 18 resources/jenkins/dsl/Releases/Release_iOS_Framework_OS.groovy | 18 resources/jenkins/dsl/Releases/Release_iOS_Framework_Simulator.groovy | 18 resources/jenkins/dsl/Releases/Release_iOS_Framework_Simulator_x86_64.groovy | 18 resources/jenkins/dsl/Releases/Release_iOS_SwiftPackage.groovy | 16 resources/jenkins/dsl/Reviews/Review_Android.groovy | 8 resources/jenkins/dsl/Reviews/Review_Formatting.groovy | 4 resources/jenkins/dsl/Reviews/Review_Libs_Android.groovy | 2 resources/jenkins/dsl/Reviews/Review_Libs_iOS.groovy | 22 resources/jenkins/dsl/Reviews/Review_Libs_iOS_OS.groovy | 22 resources/jenkins/dsl/Reviews/Review_Libs_iOS_Simulator.groovy | 22 resources/jenkins/dsl/Reviews/Review_Libs_iOS_Simulator_x86_64.groovy | 22 resources/jenkins/dsl/Reviews/Review_SonarQube.groovy | 6 resources/jenkins/dsl/Reviews/Review_Trigger.groovy | 20 resources/jenkins/dsl/Reviews/Review_Trigger_Libs.groovy | 10 resources/jenkins/dsl/Reviews/Review_Win64_GNU_MSI.groovy | 2 resources/jenkins/dsl/Reviews/Review_Win64_MSVC_MSI.groovy | 2 resources/jenkins/dsl/Reviews/Review_iOS_Framework.groovy | 18 resources/jenkins/dsl/Reviews/Review_iOS_Framework_OS.groovy | 18 resources/jenkins/dsl/Reviews/Review_iOS_Framework_Simulator.groovy | 18 resources/jenkins/dsl/Reviews/Review_iOS_Framework_Simulator_x86_64.groovy | 18 resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy | 2 resources/jenkins/dsl/Reviews/Review_iOS_SwiftPackage.groovy | 12 resources/jenkins/dsl/common/Appcast.groovy | 73 resources/jenkins/dsl/common/Build.groovy | 2 resources/jenkins/dsl/common/Constants.groovy | 8 resources/jenkins/dsl/install.py | 125 - resources/jenkins/import.py | 173 - resources/jenkins/kubernetes/android.yaml | 301 +- resources/jenkins/kubernetes/common.yaml | 116 resources/jenkins/kubernetes/docs.yaml | 75 resources/jenkins/kubernetes/format_files.sh | 29 resources/jenkins/kubernetes/linux.yaml | 113 resources/jenkins/kubernetes/redis.yaml | 56 resources/jenkins/kubernetes/sync.yaml | 67 resources/jenkins/kubernetes/trigger.yaml | 85 resources/jenkins/kubernetes/vanilla.yaml | 147 - resources/packaging/android/AndroidManifest.xml.apk.in | 2 resources/packaging/android/IAusweisApp2SdkCallback.aidl | 2 resources/packaging/android/build.gradle.append | 7 resources/packaging/android/gradle.properties.in | 2 resources/packaging/android/lint.aar.xml | 15 resources/packaging/android/lint.apk.smarteid.xml | 36 resources/packaging/android/lint.apk.xml | 36 resources/packaging/android/res/values-night/style.xml | 13 resources/packaging/ios/launchscreen.storyboard | 2 resources/packaging/linux/com.governikus.ausweisapp2.desktop.in | 2 resources/packaging/updater/Appcast.item.json.in | 10 resources/packaging/updater/Appcast.json.in | 6 resources/packaging/win/WIX.Texts.de-DE.wxl | 112 resources/packaging/win/WIX.Texts.en-US.wxl | 29 resources/packaging/win/WIX.template.in | 155 - resources/packaging/win/executable.wxs | 90 resources/packaging/win/gui.wxs | 243 +- resources/packaging/win/install_settings.wxs | 279 -- resources/packaging/win/runtime_settings.wxs | 487 +--- resources/shader/ColorOverlayShader.frag | 14 resources/shader/DesaturateShader.frag | 14 resources/sonar-project.properties.in | 29 resources/translations/ausweisapp_de.ts | 1151 ++++++--- resources/translations/ausweisapp_ru.ts | 1165 ++++++---- resources/translations/ausweisapp_uk.ts | 1149 ++++++--- resources/updatable-files/supported-providers.json | 74 resources/updatable-files/supported-readers.json | 6 ruff.toml | 18 src/CMakeLists.txt | 10 src/android/MainActivity.java | 1 src/card/base/CardConnection.cpp | 31 src/card/base/CardConnection.h | 12 src/card/base/CardConnectionWorker.cpp | 25 src/card/base/CardInfo.h | 2 src/card/base/FileRef.h | 10 src/card/base/ReaderFilter.cpp | 36 src/card/base/ReaderInfo.cpp | 147 + src/card/base/ReaderInfo.h | 173 - src/card/base/ReaderManager.cpp | 10 src/card/base/ReaderManager.h | 10 src/card/base/ReaderManagerPlugin.cpp | 85 src/card/base/ReaderManagerPlugin.h | 79 src/card/base/ReaderManagerPluginInfo.h | 2 src/card/base/ReaderManagerWorker.cpp | 37 src/card/base/ReaderManagerWorker.h | 4 src/card/base/apdu/CommandApdu.cpp | 6 src/card/base/apdu/CommandApdu.h | 1 src/card/base/apdu/ResponseApdu.h | 2 src/card/base/asn1/ASN1TemplateUtil.cpp | 9 src/card/base/asn1/ASN1TemplateUtil.h | 8 src/card/base/asn1/CVCertificate.cpp | 36 src/card/base/asn1/CVCertificate.h | 3 src/card/base/asn1/CVCertificateChainBuilder.cpp | 17 src/card/base/asn1/ChainBuilder.h | 4 src/card/base/asn1/EFCardSecurity.h | 2 src/card/base/command/UpdateRetryCounterCommand.cpp | 9 src/card/base/command/UpdateRetryCounterCommand.h | 6 src/card/base/pace/ec/EcUtil.cpp | 9 src/card/base/pinpad/EstablishPaceChannel.h | 2 src/card/base/pinpad/EstablishPaceChannelOutput.cpp | 9 src/card/base/pinpad/PinModifyOutput.cpp | 38 src/card/base/pinpad/PinModifyOutput.h | 34 src/card/drivers/DeviceListener.cpp | 1 src/card/nfc/NfcReader.cpp | 10 src/card/nfc/NfcReader.h | 2 src/card/nfc/NfcReaderManagerPlugin.cpp | 59 src/card/nfc/NfcReaderManagerPlugin.h | 8 src/card/pcsc/PcscCard.cpp | 29 src/card/pcsc/PcscReader.cpp | 6 src/card/pcsc/PcscReaderManagerPlugin.cpp | 43 src/card/pcsc/PcscReaderManagerPlugin.h | 9 src/card/simulator/SimulatorFileSystem.cpp | 10 src/card/simulator/SimulatorReaderManagerPlugin.cpp | 52 src/card/simulator/SimulatorReaderManagerPlugin.h | 8 src/card/smart/SmartReaderManagerPlugin.cpp | 43 src/card/smart/SmartReaderManagerPlugin.h | 13 src/configuration/CallCost.cpp | 4 src/configuration/LanguageString.h | 3 src/configuration/ProviderConfigurationParser.cpp | 2 src/core/controller/AppController.cpp | 9 src/crypto/Randomizer.cpp | 154 - src/crypto/Randomizer.h | 44 src/diagnosis/DiagnosisAntivirusDetection.cpp | 2 src/diagnosis/DiagnosisAntivirusDetection.h | 2 src/diagnosis/DiagnosisFirewallDetection.cpp | 8 src/diagnosis/DiagnosisModel.cpp | 2 src/diagnosis/DiagnosisModel.h | 2 src/diagnosis/SectionModel.h | 2 src/diagnosis/context/DiagnosisContext.h | 2 src/diagnosis/controller/DiagnosisController.cpp | 11 src/diagnosis/controller/DiagnosisController_osx.mm | 4 src/file_provider/Downloader.cpp | 27 src/file_provider/UpdatableFile.cpp | 4 src/global/BuildHelper.cpp | 28 src/global/BuildHelper.h | 5 src/global/ECardApiResult.cpp | 3 src/global/ECardApiResult.h | 3 src/global/Env.h | 11 src/global/FailureCode.cpp | 2 src/global/FailureCode.h | 5 src/global/GlobalStatus.cpp | 8 src/global/GlobalStatus.h | 224 - src/global/LanguageLoader.cpp | 6 src/global/LanguageLoader.h | 3 src/global/LogHandler.h | 2 src/global/UsbId.h | 2 src/ifd/base/ConnectRequest.cpp | 221 + src/ifd/base/ConnectRequest.h | 36 src/ifd/base/IfdCard.cpp | 39 src/ifd/base/IfdCard.h | 3 src/ifd/base/IfdClientImpl.cpp | 61 src/ifd/base/IfdClientImpl.h | 6 src/ifd/base/IfdConnector.cpp | 4 src/ifd/base/IfdConnector.h | 10 src/ifd/base/IfdConnectorImpl.cpp | 78 src/ifd/base/IfdConnectorImpl.h | 14 src/ifd/base/IfdDescriptor.cpp | 159 - src/ifd/base/IfdDescriptor.h | 78 src/ifd/base/IfdDispatcherClient.cpp | 2 src/ifd/base/IfdList.h | 4 src/ifd/base/IfdListEntry.cpp | 41 src/ifd/base/IfdListEntry.h | 13 src/ifd/base/IfdListImpl.cpp | 35 src/ifd/base/IfdListImpl.h | 4 src/ifd/base/IfdReaderManagerPlugin.cpp | 52 src/ifd/base/IfdReaderManagerPlugin.h | 9 src/ifd/base/ServerMessageHandlerImpl.cpp | 70 src/ifd/base/ServerMessageHandlerImpl.h | 1 src/ifd/base/TlsServer.cpp | 5 src/ifd/base/messages/Discovery.cpp | 132 + src/ifd/base/messages/Discovery.h | 13 src/ifd/base/messages/IfdConnect.h | 2 src/ifd/base/messages/IfdConnectResponse.cpp | 22 src/ifd/base/messages/IfdConnectResponse.h | 7 src/ifd/base/messages/IfdDestroyPaceChannel.cpp | 23 src/ifd/base/messages/IfdDestroyPaceChannel.h | 7 src/ifd/base/messages/IfdDestroyPaceChannelResponse.cpp | 23 src/ifd/base/messages/IfdDestroyPaceChannelResponse.h | 7 src/ifd/base/messages/IfdDisconnect.cpp | 23 src/ifd/base/messages/IfdDisconnect.h | 7 src/ifd/base/messages/IfdDisconnectResponse.cpp | 23 src/ifd/base/messages/IfdDisconnectResponse.h | 9 src/ifd/base/messages/IfdError.cpp | 23 src/ifd/base/messages/IfdError.h | 9 src/ifd/base/messages/IfdEstablishContextResponse.h | 2 src/ifd/base/messages/IfdEstablishPaceChannel.cpp | 16 src/ifd/base/messages/IfdEstablishPaceChannel.h | 5 src/ifd/base/messages/IfdEstablishPaceChannelResponse.cpp | 37 src/ifd/base/messages/IfdEstablishPaceChannelResponse.h | 8 src/ifd/base/messages/IfdModifyPin.cpp | 19 src/ifd/base/messages/IfdModifyPin.h | 7 src/ifd/base/messages/IfdModifyPinResponse.cpp | 17 src/ifd/base/messages/IfdModifyPinResponse.h | 5 src/ifd/base/messages/IfdSlotHandle.h | 62 src/ifd/base/messages/IfdTransmit.cpp | 18 src/ifd/base/messages/IfdTransmit.h | 7 src/ifd/base/messages/IfdTransmitResponse.cpp | 18 src/ifd/base/messages/IfdTransmitResponse.h | 9 src/ifd/local/CMakeLists.txt | 4 src/ifd/local/LocalIfdClient.cpp | 7 src/ifd/remote/RemoteIfdClient.cpp | 18 src/ifd/remote/RemoteIfdClient.h | 2 src/ifd/remote/RemoteIfdReaderManagerPlugin.cpp | 11 src/ifd/remote/RemoteIfdReaderManagerPlugin.h | 2 src/ifd/remote/RemoteReaderAdvertiser.cpp | 11 src/ifd/remote/RemoteTlsServer.cpp | 4 src/init/Bootstrap.cpp | 4 src/init/CMakeLists.txt | 4 src/network/DatagramHandler.cpp | 3 src/network/DatagramHandler.h | 6 src/network/DatagramHandlerImpl.cpp | 210 + src/network/DatagramHandlerImpl.h | 19 src/network/HttpRequest.cpp | 6 src/network/HttpRequest.h | 3 src/network/HttpResponse.h | 2 src/network/NetworkManager.cpp | 104 src/network/NetworkManager.h | 2 src/network/PortFile.cpp | 2 src/network/PortFile.h | 4 src/network/TlsChecker.cpp | 108 src/network/TlsChecker.h | 5 src/network/UrlUtil.cpp | 15 src/network/UrlUtil.h | 7 src/secure_storage/SecureStorage.cpp | 12 src/secure_storage/TlsConfiguration.cpp | 3 src/services/AppUpdateData.cpp | 24 src/services/AppUpdateData.h | 6 src/services/AppUpdater.cpp | 111 src/services/AppUpdater.h | 22 src/services/Service.cpp | 4 src/settings/GeneralSettings.cpp | 18 src/settings/GeneralSettings.h | 3 src/settings/KeyPair.cpp | 59 src/settings/RemoteServiceSettings.cpp | 22 src/settings/VolatileSettings.h | 2 src/ui/aidl/AidlBinder.java | 6 src/ui/aidl/UiPluginAidl.cpp | 17 src/ui/aidl/UiPluginAidl.h | 2 src/ui/json/messages/MsgHandlerStatus.cpp | 4 src/ui/proxy/PortWrapper.cpp | 34 src/ui/proxy/PortWrapper.h | 22 src/ui/proxy/PortWrapper_generic.cpp | 48 src/ui/proxy/PortWrapper_win.cpp | 111 src/ui/proxy/RedirectBroadcast.cpp | 58 src/ui/proxy/RedirectBroadcast.h | 30 src/ui/proxy/RedirectRequest.cpp | 2 src/ui/proxy/UiPluginProxy.cpp | 23 src/ui/proxy/UiPluginProxy.h | 6 src/ui/qml/AppUpdateDataModel.cpp | 129 - src/ui/qml/AppUpdateDataModel.h | 36 src/ui/qml/ApplicationModel.cpp | 54 src/ui/qml/ApplicationModel.h | 26 src/ui/qml/ApplicationModel_android.cpp | 8 src/ui/qml/ApplicationModel_generic.cpp | 1 src/ui/qml/ApplicationModel_ios.mm | 42 src/ui/qml/ApplicationModel_osx.mm | 65 src/ui/qml/AuthModel.cpp | 23 src/ui/qml/AuthModel.h | 5 src/ui/qml/CMakeLists.txt | 33 src/ui/qml/CardPosition.cpp | 57 src/ui/qml/CardPosition.h | 49 src/ui/qml/CardPositionModel.cpp | 17 src/ui/qml/CardPositionModel.h | 4 src/ui/qml/CertificateDescriptionModel.cpp | 18 src/ui/qml/CertificateDescriptionModel.h | 6 src/ui/qml/ChatModel.cpp | 2 src/ui/qml/ChatModel.h | 2 src/ui/qml/FormattedTextModel.cpp | 6 src/ui/qml/FormattedTextModel.h | 7 src/ui/qml/LogFilesModel.cpp | 143 + src/ui/qml/LogFilesModel.h | 68 src/ui/qml/LogFilterModel.cpp | 93 src/ui/qml/LogFilterModel.h | 6 src/ui/qml/LogModel.cpp | 164 - src/ui/qml/LogModel.h | 48 src/ui/qml/LogModel_android.cpp | 70 src/ui/qml/LogModel_generic.cpp | 4 src/ui/qml/LogModel_ios.mm | 56 src/ui/qml/NotificationModel.cpp | 28 src/ui/qml/NotificationModel.h | 10 src/ui/qml/NumberModel.cpp | 94 src/ui/qml/PinResetInformationModel.cpp | 28 src/ui/qml/PinResetInformationModel.h | 8 src/ui/qml/ReaderModel.cpp | 2 src/ui/qml/ReaderModel.h | 2 src/ui/qml/RemoteDeviceModel.cpp | 14 src/ui/qml/RemoteDeviceModel.h | 2 src/ui/qml/RemoteDeviceModelEntry.cpp | 8 src/ui/qml/RemoteServiceModel.cpp | 2 src/ui/qml/SelfAuthModel.cpp | 4 src/ui/qml/SelfAuthModel.h | 2 src/ui/qml/SettingsModel.cpp | 62 src/ui/qml/SettingsModel.h | 20 src/ui/qml/SurveyModel.cpp | 4 src/ui/qml/SurveyModel.h | 2 src/ui/qml/UiPluginModel.cpp | 14 src/ui/qml/UiPluginModel.h | 12 src/ui/qml/UiPluginQml.cpp | 201 - src/ui/qml/UiPluginQml.h | 22 src/ui/qml/UiPluginQml_android.cpp | 70 src/ui/qml/UiPluginQml_generic.cpp | 18 src/ui/qml/UiPluginQml_ios.mm | 51 src/ui/qml/UiPluginQml_osx.mm | 18 src/ui/qml/VersionInformationModel.cpp | 4 src/ui/qml/VersionInformationModel.h | 7 src/ui/qml/WorkflowModel.cpp | 18 src/ui/qml/WorkflowModel.h | 6 src/ui/qml/metadata.json | 8 src/ui/qml/modules/Animations/+6.5/HourglassAnimation.qml | 11 src/ui/qml/modules/Animations/AnimationLoader.qml | 42 src/ui/qml/modules/Animations/HourglassAnimation.qml | 2 src/ui/qml/modules/Animations/LinkQualityAnimation.qml | 6 src/ui/qml/modules/Animations/SpinningLoader.qml | 4 src/ui/qml/modules/Animations/WorkflowAnimationLoader.qml | 16 src/ui/qml/modules/AuthView/+desktop/EditRights.qml | 5 src/ui/qml/modules/AuthView/+desktop/TransportPinReminderView.qml | 6 src/ui/qml/modules/AuthView/+desktop/internal/AuthController.qml | 116 src/ui/qml/modules/AuthView/+desktop/internal/DataGroup.qml | 15 src/ui/qml/modules/AuthView/+desktop/internal/SelfAuthenticationData.qml | 27 src/ui/qml/modules/AuthView/+mobile/AbortedProgressView.qml | 1 src/ui/qml/modules/AuthView/+mobile/CardPositionView.qml | 2 src/ui/qml/modules/AuthView/+mobile/EditRights.qml | 11 src/ui/qml/modules/AuthView/+mobile/TransportPinReminderView.qml | 2 src/ui/qml/modules/AuthView/+mobile/internal/AuthController.qml | 100 src/ui/qml/modules/AuthView/+mobile/internal/DataGroup.qml | 6 src/ui/qml/modules/AuthView/+mobile/internal/SelfAuthenticationData.qml | 17 src/ui/qml/modules/AuthView/AuthCanceledView.qml | 86 src/ui/qml/modules/AuthView/ProviderInfo.qml | 18 src/ui/qml/modules/AuthView/internal/AutoRedirectDecision.qml | 40 src/ui/qml/modules/AuthView/internal/BaseTransportPinReminderView.qml | 14 src/ui/qml/modules/AuthView/internal/RedirectView.qml | 27 src/ui/qml/modules/AuthView/internal/SelfAuthenticationHeader.qml | 29 src/ui/qml/modules/CMakeLists.txt | 3 src/ui/qml/modules/ChangePinView/+desktop/ChangePinController.qml | 35 src/ui/qml/modules/ChangePinView/+desktop/ChangePinView.qml | 16 src/ui/qml/modules/ChangePinView/+mobile/ChangePinController.qml | 73 src/ui/qml/modules/ChangePinView/+mobile/ChangePinView.qml | 2 src/ui/qml/modules/ChangePinView/ChangePinViewContent.qml | 4 src/ui/qml/modules/ChangePinView/ChangeTransportPinInfoView.qml | 14 src/ui/qml/modules/ChangePinView/internal/ChangePinInfoView.qml | 14 src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardNoNfcSuggestion.qml | 2 src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardView.qml | 3 src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardWorkflow.qml | 27 src/ui/qml/modules/CheckIDCardView/CheckIDCardResultView.qml | 18 src/ui/qml/modules/CheckIDCardView/CheckIDCardSuggestionView.qml | 38 src/ui/qml/modules/CheckResultView/CheckResultSuggestionView.qml | 2 src/ui/qml/modules/CheckResultView/SuggestionData.qml | 2 src/ui/qml/modules/EnterPasswordView/EnterPasswordView.qml | 76 src/ui/qml/modules/EnterPasswordView/internal/NumberPad.qml | 150 - src/ui/qml/modules/EnterPasswordView/internal/NumberPadButton.qml | 3 src/ui/qml/modules/FeedbackView/+desktop/LogView.qml | 39 src/ui/qml/modules/FeedbackView/+desktop/internal/DetachedLogView.qml | 63 src/ui/qml/modules/FeedbackView/+desktop/internal/LogViewDelegate.qml | 3 src/ui/qml/modules/FeedbackView/+mobile/+ios/StoreFeedbackPopup.qml | 2 src/ui/qml/modules/FeedbackView/+mobile/ListItem.qml | 117 - src/ui/qml/modules/FeedbackView/+mobile/LogFilesView.qml | 78 src/ui/qml/modules/FeedbackView/+mobile/LogView.qml | 141 - src/ui/qml/modules/FeedbackView/+mobile/internal/LogTitleBarControls.qml | 33 src/ui/qml/modules/FeedbackView/+mobile/internal/LogViewDelegate.qml | 86 src/ui/qml/modules/Global/+6.5/GDropShadow.qml | 12 src/ui/qml/modules/Global/+6.7/GColorOverlay.qml | 12 src/ui/qml/modules/Global/+6.7/GDesaturate.qml | 9 src/ui/qml/modules/Global/+desktop/CardNotActivatedView.qml | 31 src/ui/qml/modules/Global/+desktop/ConfirmationPopup.qml | 22 src/ui/qml/modules/Global/+desktop/GFileDialog.qml | 14 src/ui/qml/modules/Global/+desktop/GMenuItem.qml | 14 src/ui/qml/modules/Global/+desktop/GPane.qml | 54 src/ui/qml/modules/Global/+desktop/Hint.qml | 20 src/ui/qml/modules/Global/+desktop/LocationButton.qml | 66 src/ui/qml/modules/Global/+desktop/ProxyCredentialsPopup.qml | 74 src/ui/qml/modules/Global/+desktop/ReaderDetection.qml | 43 src/ui/qml/modules/Global/+desktop/TabbedPane.qml | 14 src/ui/qml/modules/Global/+desktop/TabbedPaneDelegate.qml | 22 src/ui/qml/modules/Global/+mobile/CardNotActivatedView.qml | 31 src/ui/qml/modules/Global/+mobile/GCollapsibleSubButton.qml | 63 src/ui/qml/modules/Global/+mobile/GMenuItem.qml | 17 src/ui/qml/modules/Global/+mobile/GOptionsContainer.qml | 31 src/ui/qml/modules/Global/+mobile/GPane.qml | 46 src/ui/qml/modules/Global/+mobile/Hint.qml | 44 src/ui/qml/modules/Global/+mobile/PaneTitle.qml | 12 src/ui/qml/modules/Global/BaseConfirmationPopup.qml | 19 src/ui/qml/modules/Global/Crossed.qml | 2 src/ui/qml/modules/Global/DecisionView.qml | 8 src/ui/qml/modules/Global/FormattedTextView.qml | 111 src/ui/qml/modules/Global/GAbstractButton.qml | 18 src/ui/qml/modules/Global/GButton.qml | 8 src/ui/qml/modules/Global/GCheckBox.qml | 2 src/ui/qml/modules/Global/GCollapsible.qml | 50 src/ui/qml/modules/Global/GColorOverlay.qml | 11 src/ui/qml/modules/Global/GComboBox.qml | 91 src/ui/qml/modules/Global/GCrossBlendedText.qml | 2 src/ui/qml/modules/Global/GDesaturate.qml | 9 src/ui/qml/modules/Global/GFlickable.qml | 88 src/ui/qml/modules/Global/GInformativeButton.qml | 8 src/ui/qml/modules/Global/GLink.qml | 9 src/ui/qml/modules/Global/GListView.qml | 74 src/ui/qml/modules/Global/GPane.qml | 57 src/ui/qml/modules/Global/GProgressBar.qml | 57 src/ui/qml/modules/Global/GRadioButton.qml | 94 src/ui/qml/modules/Global/GSeparator.qml | 4 src/ui/qml/modules/Global/GStagedProgressBar.qml | 2 src/ui/qml/modules/Global/GSwitch.qml | 118 - src/ui/qml/modules/Global/GText.qml | 14 src/ui/qml/modules/Global/GTextField.qml | 7 src/ui/qml/modules/Global/Heading.qml | 14 src/ui/qml/modules/Global/LabeledText.qml | 10 src/ui/qml/modules/Global/NumberField.qml | 74 src/ui/qml/modules/Global/PaneTitle.qml | 18 src/ui/qml/modules/Global/PkiSwitch.qml | 16 src/ui/qml/modules/Global/PrivacyStatement.qml | 5 src/ui/qml/modules/Global/ProxyCredentialsPopup.qml | 74 src/ui/qml/modules/Global/Subheading.qml | 11 src/ui/qml/modules/Global/TintableIcon.qml | 15 src/ui/qml/modules/Global/Utils.qml | 70 src/ui/qml/modules/Global/UtilsIOS.qml | 82 src/ui/qml/modules/Global/internal/BaseHeading.qml | 16 src/ui/qml/modules/Global/internal/CardNotActivatedBaseView.qml | 175 + src/ui/qml/modules/InformationView/+desktop/DiagnosisView.qml | 6 src/ui/qml/modules/InformationView/+desktop/LicenseInformation.qml | 42 src/ui/qml/modules/InformationView/+desktop/VersionInformation.qml | 14 src/ui/qml/modules/InformationView/+mobile/LicenseInformation.qml | 6 src/ui/qml/modules/InformationView/+mobile/VersionInformation.qml | 18 src/ui/qml/modules/InformationView/ReleaseNotesView.qml | 36 src/ui/qml/modules/Init/+desktop/App.qml | 64 src/ui/qml/modules/Init/+desktop/internal/CloseHandler.qml | 127 - src/ui/qml/modules/Init/+mobile/App.qml | 63 src/ui/qml/modules/MainView/+desktop/internal/Tile.qml | 11 src/ui/qml/modules/MainView/+mobile/MainView.qml | 14 src/ui/qml/modules/MainView/+mobile/internal/Tile.qml | 4 src/ui/qml/modules/MoreView/+desktop/MoreView.qml | 4 src/ui/qml/modules/MoreView/+desktop/internal/MoreViewDiagnosis.qml | 3 src/ui/qml/modules/MoreView/+desktop/internal/MoreViewGeneral.qml | 16 src/ui/qml/modules/MoreView/+mobile/MoreView.qml | 138 - src/ui/qml/modules/MultiInfoView/+desktop/MultiInfoView.qml | 26 src/ui/qml/modules/MultiInfoView/+mobile/MultiInfoView.qml | 10 src/ui/qml/modules/MultiInfoView/MultiInfoData.qml | 42 src/ui/qml/modules/MultiInfoView/internal/MultiInfoContentBlock.qml | 4 src/ui/qml/modules/Navigation/+mobile/Navigation.qml | 51 src/ui/qml/modules/Navigation/+mobile/internal/NavigationItem.qml | 4 src/ui/qml/modules/OnboardingView/+desktop/ChooseReaderType.qml | 7 src/ui/qml/modules/OnboardingView/+desktop/OnboardingCheckIDView.qml | 50 src/ui/qml/modules/OnboardingView/+desktop/OnboardingConfirmStageView.qml | 2 src/ui/qml/modules/OnboardingView/+desktop/OnboardingConnectUsbReaderView.qml | 20 src/ui/qml/modules/OnboardingView/+desktop/OnboardingPairSacStartView.qml | 15 src/ui/qml/modules/OnboardingView/+desktop/OnboardingPairSacView.qml | 37 src/ui/qml/modules/OnboardingView/+desktop/OnboardingView.qml | 35 src/ui/qml/modules/OnboardingView/+desktop/PreparationInfoView.qml | 26 src/ui/qml/modules/OnboardingView/+desktop/SkipOnboardingConfirmation.qml | 2 src/ui/qml/modules/OnboardingView/+mobile/OnboardingCheckIDView.qml | 46 src/ui/qml/modules/OnboardingView/+mobile/OnboardingView.qml | 21 src/ui/qml/modules/OnboardingView/+mobile/PreparationInfoView.qml | 18 src/ui/qml/modules/OnboardingView/+mobile/PrimaryDeviceDecisionView.qml | 6 src/ui/qml/modules/OnboardingView/+mobile/SetupDesktopUsage.qml | 16 src/ui/qml/modules/OnboardingView/OnboardingConfirmStageBaseView.qml | 12 src/ui/qml/modules/OnboardingView/OnboardingConfirmationViewData.qml | 12 src/ui/qml/modules/OnboardingView/OnboardingFailedView.qml | 10 src/ui/qml/modules/OnboardingView/OnboardingStartView.qml | 9 src/ui/qml/modules/ProgressView/+desktop/ProgressView.qml | 9 src/ui/qml/modules/ProgressView/+mobile/ProgressView.qml | 7 src/ui/qml/modules/RemoteServiceView/+mobile/RemoteServiceSettings.qml | 38 src/ui/qml/modules/RemoteServiceView/+mobile/RemoteServiceView.qml | 455 ++- src/ui/qml/modules/RemoteServiceView/+mobile/internal/DevicesListDelegate.qml | 70 src/ui/qml/modules/RemoteServiceView/+mobile/internal/PairingCodeInfoView.qml | 77 src/ui/qml/modules/RemoteServiceView/+mobile/internal/RemoteServiceWifiInfo.qml | 29 src/ui/qml/modules/RemoteServiceView/PairingFailedView.qml | 2 src/ui/qml/modules/RemoteServiceView/PairingSuccessView.qml | 2 src/ui/qml/modules/ResultView/+desktop/ReaderFoundConfirmation.qml | 46 src/ui/qml/modules/ResultView/+desktop/ResultView.qml | 29 src/ui/qml/modules/ResultView/+mobile/ResultErrorView.qml | 5 src/ui/qml/modules/ResultView/+mobile/ResultView.qml | 10 src/ui/qml/modules/ResultView/InputErrorView.qml | 19 src/ui/qml/modules/ResultView/InputSuccessView.qml | 25 src/ui/qml/modules/SelfAuthenticationView/+desktop/SelfAuthenticationView.qml | 4 src/ui/qml/modules/SettingsView/+desktop/CardReaderView.qml | 20 src/ui/qml/modules/SettingsView/+desktop/ConnectSacView.qml | 2 src/ui/qml/modules/SettingsView/+desktop/DarkModeButtons.qml | 90 src/ui/qml/modules/SettingsView/+desktop/LanguageButtons.qml | 21 src/ui/qml/modules/SettingsView/+desktop/RemoteReaderDelegate.qml | 84 src/ui/qml/modules/SettingsView/+desktop/RemoteReaderView.qml | 10 src/ui/qml/modules/SettingsView/+desktop/SettingsView.qml | 16 src/ui/qml/modules/SettingsView/+desktop/TabbedReaderView.qml | 61 src/ui/qml/modules/SettingsView/+desktop/internal/CardReaderDelegate.qml | 8 src/ui/qml/modules/SettingsView/+desktop/internal/DebugSettings.qml | 3 src/ui/qml/modules/SettingsView/+desktop/internal/DeveloperSettings.qml | 24 src/ui/qml/modules/SettingsView/+desktop/internal/GRadioButton.qml | 128 + src/ui/qml/modules/SettingsView/+desktop/internal/GRadioButtonFocusFrame.qml | 49 src/ui/qml/modules/SettingsView/+desktop/internal/GeneralSettings.qml | 79 src/ui/qml/modules/SettingsView/+desktop/internal/SecurityAndPrivacySettings.qml | 58 src/ui/qml/modules/SettingsView/+mobile/DarkModeButtons.qml | 76 src/ui/qml/modules/SettingsView/+mobile/LanguageButtons.qml | 32 src/ui/qml/modules/SettingsView/+mobile/SettingsView.qml | 179 - src/ui/qml/modules/SettingsView/+mobile/internal/GRadioButton.qml | 73 src/ui/qml/modules/SettingsView/+mobile/internal/GRadioGroup.qml | 20 src/ui/qml/modules/SettingsView/+mobile/internal/TechnologySwitch.qml | 76 src/ui/qml/modules/SettingsView/internal/DarkModeButtonData.qml | 33 src/ui/qml/modules/SettingsView/internal/LanguageButtonData.qml | 36 src/ui/qml/modules/SmartView/+mobile/internal/CheckSmartResultView.qml | 4 src/ui/qml/modules/SmartView/+mobile/internal/PersonalizationController.qml | 1 src/ui/qml/modules/SmartView/+mobile/internal/PersonalizationResultView.qml | 4 src/ui/qml/modules/SmartView/+mobile/internal/SmartDeleteBaseView.qml | 9 src/ui/qml/modules/SmartView/+mobile/internal/SmartMainView.qml | 7 src/ui/qml/modules/SmartView/+mobile/internal/SmartSettingsView.qml | 29 src/ui/qml/modules/SmartView/+mobile/internal/SmartSetupStartView.qml | 2 src/ui/qml/modules/SmartView/+mobile/internal/SmartUpdateStartView.qml | 5 src/ui/qml/modules/Style/+desktop/internal/PlatformColorsContrast.qml | 14 src/ui/qml/modules/Style/+desktop/internal/PlatformDimensions.qml | 88 src/ui/qml/modules/Style/+mobile/internal/PlatformDimensions.qml | 5 src/ui/qml/modules/Style/FontStyle.qml | 21 src/ui/qml/modules/Style/Style.qml | 5 src/ui/qml/modules/Style/TextStyle.qml | 2 src/ui/qml/modules/Style/internal/Colors.qml | 1 src/ui/qml/modules/Style/internal/ColorsDark.qml | 6 src/ui/qml/modules/Style/internal/ColorsLight.qml | 7 src/ui/qml/modules/Style/internal/TextStyles.qml | 13 src/ui/qml/modules/TitleBar/+desktop/NavigationAction.qml | 24 src/ui/qml/modules/TitleBar/+desktop/TitleBar.qml | 29 src/ui/qml/modules/TitleBar/+desktop/TitleBarSettings.qml | 2 src/ui/qml/modules/TitleBar/+desktop/internal/Notifications.qml | 28 src/ui/qml/modules/TitleBar/+desktop/internal/TitleBarButton.qml | 20 src/ui/qml/modules/TitleBar/+mobile/NavigationAction.qml | 3 src/ui/qml/modules/TitleBar/+mobile/TitleBar.qml | 42 src/ui/qml/modules/TitleBar/+mobile/TitleBarAction.qml | 4 src/ui/qml/modules/TitleBar/+mobile/TitleBarNavigation.qml | 8 src/ui/qml/modules/UpdateView/+desktop/UpdateView.qml | 142 - src/ui/qml/modules/UpdateView/+desktop/internal/UpdateViewButtonRow.qml | 57 src/ui/qml/modules/UpdateView/+desktop/internal/UpdateViewInformation.qml | 17 src/ui/qml/modules/View/+desktop/ContentArea.qml | 11 src/ui/qml/modules/View/+desktop/FocusPoint.qml | 23 src/ui/qml/modules/View/+desktop/SectionPage.qml | 7 src/ui/qml/modules/View/+mobile/ContentArea.qml | 5 src/ui/qml/modules/View/+mobile/FocusPoint.qml | 10 src/ui/qml/modules/View/+mobile/SectionPage.qml | 4 src/ui/qml/modules/View/+mobile/TabBarView.qml | 2 src/ui/qml/modules/View/FlickableSectionPage.qml | 6 src/ui/qml/modules/View/FocusFrame.qml | 10 src/ui/qml/modules/View/internal/BaseController.qml | 26 src/ui/qml/modules/Workflow/+desktop/GeneralWorkflow.qml | 49 src/ui/qml/modules/Workflow/+desktop/internal/ProgressCircle.qml | 2 src/ui/qml/modules/Workflow/+mobile/GeneralWorkflow.qml | 2 src/ui/qml/modules/Workflow/+mobile/NfcConnectionInfoView.qml | 25 src/ui/qml/modules/Workflow/+mobile/NfcWorkflow.qml | 15 src/ui/qml/modules/Workflow/+mobile/RemoteWorkflow.qml | 2 src/ui/qml/modules/Workflow/+mobile/SimulatorWorkflow.qml | 2 src/ui/qml/modules/Workflow/+mobile/SmartWorkflow.qml | 2 src/ui/qml/modules/Workflow/+mobile/internal/RemoteProgressIndicator.qml | 2 src/ui/qml/modules/Workflow/+mobile/internal/TechnologyInfo.qml | 8 src/workflows/base/Survey.cpp | 18 src/workflows/base/Survey.h | 11 src/workflows/base/WorkflowRequest.h | 3 src/workflows/base/context/AuthContext.cpp | 44 src/workflows/base/context/AuthContext.h | 17 src/workflows/base/context/ChangePinContext.cpp | 2 src/workflows/base/context/WorkflowContext.h | 4 src/workflows/base/controller/AuthController.cpp | 2 src/workflows/base/paos/element/ElementParser.cpp | 11 src/workflows/base/paos/element/ElementParser.h | 7 src/workflows/base/paos/retrieve/DidAuthenticateEac1Parser.cpp | 2 src/workflows/base/states/AbstractState.cpp | 2 src/workflows/base/states/AbstractState.h | 2 src/workflows/base/states/StateCertificateDescriptionCheck.cpp | 6 src/workflows/base/states/StateChangePin.cpp | 7 src/workflows/base/states/StateCheckRefreshAddress.cpp | 2 src/workflows/base/states/StateExtractCvcsFromEac1InputType.cpp | 2 src/workflows/base/states/StateGenericSendReceive.cpp | 68 src/workflows/base/states/StateGenericSendReceive.h | 3 src/workflows/base/states/StateGetTcToken.cpp | 79 src/workflows/base/states/StateGetTcToken.h | 2 src/workflows/base/states/StatePreVerification.cpp | 9 src/workflows/base/states/StateRedirectBrowser.cpp | 10 src/workflows/base/states/StateRedirectBrowser.h | 1 src/workflows/ifd/states/StateEnterNewPacePinIfd.cpp | 15 src/workflows/ifd/states/StateEnterNewPacePinIfd.h | 1 src/workflows/ifd/states/StateEnterPacePasswordIfd.cpp | 15 src/workflows/ifd/states/StateEnterPacePasswordIfd.h | 1 src/workflows/selfauth/SelfAuthenticationData.cpp | 14 src/workflows/selfauth/SelfAuthenticationData.h | 9 src/workflows/selfauth/context/SelfAuthContext.cpp | 2 test/CMakeLists.txt | 15 test/fixture/card/simulatorFiles.json | 6 test/fixture/core/diagnosis/firstRuleDisabled.txt | 4 test/fixture/core/diagnosis/first_Rule.txt | 2 test/fixture/updatable-files/supported-providers_no-prs.json | 2 test/fixture/updatable-files/supported-providers_prs.json | 2 test/helper/common/CMakeLists.txt | 4 test/helper/common/MockCard.cpp | 12 test/helper/common/MockCard.h | 8 test/helper/common/MockNetworkReply.h | 19 test/helper/common/MockReaderManagerPlugin.cpp | 51 test/helper/common/MockReaderManagerPlugin.h | 9 test/helper/ifd/MockIfdDispatcher.cpp | 64 test/helper/ifd/MockIfdDispatcher.h | 3 test/helper/ui/json/MsgHandlerEnterPassword.cpp | 5 test/helper/ui/websocket/WebSocketHelper.cpp | 6 test/helper/ui/websocket/WebSocketHelper.h | 2 test/integrated/CMakeLists.txt | 2 test/integrated/test_Integrated.cpp | 10 test/integration/init/test_CommandLineParser.cpp | 4 test/integration/ui/qml/test_Qml.cpp | 2 test/qml/+mobile/test_ProgressView.qml | 2 test/qml/AuthView/+desktop/test_AuthView.qml | 3 test/qml/AuthView/+mobile/test_AuthView.qml | 3 test/qml/AuthView/test_AuthModel.qml | 33 test/qml/CMakeLists.txt | 3 test/qml/FeedbackView/+mobile/test_ListItem.qml | 54 test/qml/FeedbackView/+mobile/test_LogFilesView.qml | 43 test/qml/FeedbackView/+mobile/test_LogView.qml | 3 test/qml/Global/+desktop/test_GFileDialog.qml | 8 test/qml/Global/+desktop/test_LocationButton.qml | 29 test/qml/Global/+desktop/test_ProxyCredentialsPopup.qml | 85 test/qml/Global/+desktop/test_TabbedPane.qml | 6 test/qml/Global/+mobile/test_GCollapsibleSubButton.qml | 31 test/qml/Global/test_GComboBox.qml | 66 test/qml/Global/test_GFlickable.qml | 4 test/qml/Global/test_GRadioButton.qml | 64 test/qml/Global/test_ProxyCredentialsPopup.qml | 85 test/qml/PasswordInfoView/test_MultiInfoView.qml | 1 test/qml/QmlTestRunner.cpp | 18 test/qml/View/+mobile/test_ContentArea.qml | 3 test/qml/View/test_FocusPoint.qml | 21 test/qt/card/asn1/test_AccessRoleAndRight.cpp | 2 test/qt/card/asn1/test_AuthenticatedAuxiliaryData.cpp | 10 test/qt/card/asn1/test_CVCertificate.cpp | 57 test/qt/card/asn1/test_CertificateDescription.cpp | 2 test/qt/card/asn1/test_ChainBuilder.cpp | 15 test/qt/card/asn1/test_Chat.cpp | 2 test/qt/card/asn1/test_Oid.cpp | 8 test/qt/card/asn1/test_PaceInfo.cpp | 4 test/qt/card/asn1/test_SecurityInfos.cpp | 4 test/qt/card/asn1/test_SignatureChecker.cpp | 16 test/qt/card/base/apdu/test_GeneralAuthenticateResponse.cpp | 6 test/qt/card/base/apdu/test_PacePinStatus.cpp | 2 test/qt/card/base/apdu/test_ResponseApdu.cpp | 2 test/qt/card/base/apdu/test_SecureMessaging.cpp | 4 test/qt/card/base/command/test_TransmitCommand.cpp | 2 test/qt/card/base/command/test_UpdRetryCounterCommand.cpp | 14 test/qt/card/base/pinpad/test_EstablishPaceChannelOutput.cpp | 2 test/qt/card/base/pinpad/test_PinModifyOutput.cpp | 53 test/qt/card/base/test_Card.cpp | 6 test/qt/card/base/test_CardConnection.cpp | 4 test/qt/card/base/test_CardConnectionWorker.cpp | 63 test/qt/card/base/test_CardInfo.cpp | 2 test/qt/card/base/test_ReaderManager.cpp | 48 test/qt/card/base/test_ReaderManagerPlugin.cpp | 91 test/qt/card/ifd/test_IfdCard.cpp | 18 test/qt/card/pace/test_PaceHandler.cpp | 2 test/qt/card/pcsc/test_PcscReaderManagerPlugin.cpp | 6 test/qt/card/simulator/test_SimulatorReaderManagerPlugin.cpp | 122 + test/qt/card/smart/test_SmartReaderManagerPlugin.cpp | 13 test/qt/configuration/test_ProviderConfiguration.cpp | 58 test/qt/configuration/test_ProviderConfigurationParser.cpp | 14 test/qt/configuration/test_ReaderConfiguration.cpp | 4 test/qt/configuration/test_ReleaseInformation.cpp | 8 test/qt/core/test_AppController.cpp | 28 test/qt/crypto/test_Randomizer.cpp | 50 test/qt/diagnosis/test_DiagnosisAntivirusDetection.cpp | 20 test/qt/diagnosis/test_DiagnosisFirewallDetection.cpp | 2 test/qt/diagnosis/test_DiagnosisModel.cpp | 4 test/qt/global/test_CardReturnCode.cpp | 1 test/qt/global/test_ECardApiResult.cpp | 2 test/qt/global/test_EnumHelper.cpp | 12 test/qt/global/test_GlobalStatus.cpp | 4 test/qt/global/test_LogHandler.cpp | 6 test/qt/global/test_VersionInfo.cpp | 24 test/qt/ifd/messages/test_Discovery.cpp | 399 ++- test/qt/ifd/messages/test_IfdEstablishPaceChannelResponse.cpp | 31 test/qt/ifd/remote/test_RemoteIfdClient.cpp | 177 + test/qt/ifd/remote/test_RemoteIfdReaderManagerPlugin.cpp | 139 - test/qt/ifd/remote/test_RemoteReaderAdvertiser.cpp | 23 test/qt/ifd/remote/test_RemoteTlsServer.cpp | 12 test/qt/ifd/remote/test_RemoteWebSocketServer.cpp | 10 test/qt/ifd/test_ConnectRequest.cpp | 233 ++ test/qt/ifd/test_IfdConnector.cpp | 119 - test/qt/ifd/test_IfdDescriptor.cpp | 94 test/qt/ifd/test_IfdListImpl.cpp | 26 test/qt/ifd/test_ServerMessageHandler.cpp | 31 test/qt/network/test_DatagramHandlerImpl.cpp | 82 test/qt/network/test_NetworkManager.cpp | 50 test/qt/network/test_TlsChecker.cpp | 8 test/qt/network/test_UrlUtil.cpp | 76 test/qt/services/test_AppUpdatData.cpp | 40 test/qt/services/test_AppUpdatr.cpp | 34 test/qt/settings/test_GeneralSettings.cpp | 18 test/qt/ui/automatic/test_UiPluginAutomatic.cpp | 16 test/qt/ui/json/test_Message.cpp | 2 test/qt/ui/json/test_MsgHandlerInsertCard.cpp | 9 test/qt/ui/json/test_MsgHandlerReader.cpp | 10 test/qt/ui/json/test_MsgHandlerReaderList.cpp | 8 test/qt/ui/json/test_MsgHandlerStatus.cpp | 4 test/qt/ui/json/test_UiLoader.cpp | 2 test/qt/ui/json/test_UiPluginJson.cpp | 4 test/qt/ui/proxy/test_RedirectBroadcast.cpp | 55 test/qt/ui/proxy/test_UiPluginProxy.cpp | 20 test/qt/ui/qml/smart/test_SmartModel.cpp | 10 test/qt/ui/qml/test_AppUpdateDataModel.cpp | 29 test/qt/ui/qml/test_ApplicationModel.cpp | 66 test/qt/ui/qml/test_AuthModel.cpp | 55 test/qt/ui/qml/test_CardPosition.cpp | 52 test/qt/ui/qml/test_CardPositionModel.cpp | 27 test/qt/ui/qml/test_CertificateDescriptionModel.cpp | 6 test/qt/ui/qml/test_Email.cpp | 12 test/qt/ui/qml/test_FormattedTextModel.cpp | 45 test/qt/ui/qml/test_LogFilesModel.cpp | 118 + test/qt/ui/qml/test_LogFilterModel.cpp | 28 test/qt/ui/qml/test_LogModel.cpp | 108 test/qt/ui/qml/test_NotificationModel.cpp | 10 test/qt/ui/qml/test_NumberModel.cpp | 12 test/qt/ui/qml/test_PinResetInformationModel.cpp | 59 test/qt/ui/qml/test_ReaderModel.cpp | 14 test/qt/ui/qml/test_ReleaseInformationModel.cpp | 14 test/qt/ui/qml/test_RemoteDeviceModel.cpp | 90 test/qt/ui/qml/test_SettingsModel.cpp | 84 test/qt/ui/qml/test_UiPluginQml.cpp | 63 test/qt/ui/qml/test_WorkflowModel.cpp | 29 test/qt/ui/webservice/test_UiPluginWebService.cpp | 7 test/qt/workflows/context/test_AuthContext.cpp | 2 test/qt/workflows/ifd/test_StateEnterNewPacePinIfd.cpp | 37 test/qt/workflows/ifd/test_StateEnterPacePasswordIfd.cpp | 37 test/qt/workflows/ifd/test_StateProcessIfdMessages.cpp | 4 test/qt/workflows/paos/retrieve/test_DidAuthenticateEac1.cpp | 2 test/qt/workflows/selfauth/test_SelfAuthenticationData.cpp | 34 test/qt/workflows/states/test_StateChangePin.cpp | 2 test/qt/workflows/states/test_StateCheckRefreshAddress.cpp | 33 test/qt/workflows/states/test_StateDidAuthenticateEac2.cpp | 4 test/qt/workflows/states/test_StateGenericProviderCommunication.cpp | 5 test/qt/workflows/states/test_StateGenericSendReceive.cpp | 83 test/qt/workflows/states/test_StateGetTcToken.cpp | 92 test/qt/workflows/states/test_StateMaintainCardConnection.cpp | 2 test/qt/workflows/states/test_StatePreVerification.cpp | 12 test/qt/workflows/states/test_StateRedirectBrowser.cpp | 17 test/qt/workflows/states/test_TermsOfUsage.cpp | 8 test/qt/workflows/test_TcToken.cpp | 96 test/tools/CMakeLists.txt | 55 test/tools/checkQmlResources.py | 172 - test/tools/qmllint/CMakeLists.txt | 14 test/tools/qmllint/ElementPass.cpp | 30 test/tools/qmllint/ElementPass.h | 21 test/tools/qmllint/Plugin.cpp | 19 test/tools/qmllint/Plugin.h | 23 test/tools/qmllint/metadata.json | 13 uncrustify.cfg | 16 1029 files changed, 30244 insertions(+), 19524 deletions(-) diff -Nru ausweisapp2-2.3.1/.codespellrc ausweisapp2-2.4.0/.codespellrc --- ausweisapp2-2.3.1/.codespellrc 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/.codespellrc 2025-10-30 10:10:48.000000000 +0000 @@ -1,2 +1,3 @@ [codespell] -skip = ./docs/releasenotes/*,*.pdf,*.ts,*.wxl,*.svg,./src/external/*,.git,.hg,LICENSE*,CMakeLists.txt.user,patch.py,README.rst,README.de.rst,supported-readers.json,supported-providers.json,./test/fixture/logfiles/* +skip = ./docs/*,./resources/*,./libs/*,./test/fixture/*,./src/external/*,LICENSE*,README.rst,CONTRIBUTING.rst +ignore-words-list = bund,pEvent,requestor,uptodate,pAdd diff -Nru ausweisapp2-2.3.1/.github/dependabot.yml ausweisapp2-2.4.0/.github/dependabot.yml --- ausweisapp2-2.3.1/.github/dependabot.yml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/.github/dependabot.yml 2025-10-30 10:10:48.000000000 +0000 @@ -6,7 +6,7 @@ version: 2 updates: - # Maintain dependencies for GitHub Actions + # Maintain dependencies for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: diff -Nru ausweisapp2-2.3.1/.github/workflows/CI_build.yml ausweisapp2-2.4.0/.github/workflows/CI_build.yml --- ausweisapp2-2.3.1/.github/workflows/CI_build.yml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/.github/workflows/CI_build.yml 2025-10-30 10:10:48.000000000 +0000 @@ -1,6 +1,6 @@ name: CI_build -on: [push, pull_request] +"on": [push, pull_request] jobs: @@ -15,36 +15,36 @@ steps: - - name: Install openssl dev - run: | - choco install openssl --version=3.4.1 - choco install ninja - - - name: Add nmake - uses: ilammy/msvc-dev-cmd@v1 - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Install Qt - uses: jurplel/install-qt-action@v4 - with: - version: '6.8.*' - modules: 'qtscxml qtwebsockets qtshadertools qtconnectivity qtimageformats' - setup-python: 'false' - - - name: generate cmake - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B _build - - - name: build cmake - run: | - cmake --build _build --config ${{ matrix.build_configuration }} --target package - cmake --install _build - - - name: run ctest - run: | - ctest --test-dir _build --output-on-failure -C "${{ matrix.build_configuration }}" + - name: Install openssl dev + run: | + choco install openssl --version=3.4.1 + choco install ninja + + - name: Add nmake + uses: ilammy/msvc-dev-cmd@v1 + + - name: Checkout repo + uses: actions/checkout@v5 + + - name: Install Qt + uses: jurplel/install-qt-action@v4 + with: + version: '6.8.*' + modules: 'qtscxml qtwebsockets qtshadertools qtconnectivity qtimageformats' + setup-python: 'false' + + - name: generate cmake + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B _build + + - name: build cmake + run: | + cmake --build _build --config ${{ matrix.build_configuration }} --target package + cmake --install _build + + - name: run ctest + run: | + ctest --test-dir _build --output-on-failure -C "${{ matrix.build_configuration }}" build_linux: @@ -56,31 +56,32 @@ build_platform: ["Unix Makefiles"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - name: Install packages via apt - run: | - sudo apt update -qq && sudo apt install -y cmake pkg-config libssl-dev libudev-dev libhttp-parser-dev libpcsclite-dev libgl1-mesa-dev qt6-l10n-tools - - - name: Install Qt - uses: jurplel/install-qt-action@v4 - with: - version: '6.8.1' - modules: 'qtscxml qtwebsockets qtshadertools qtconnectivity' - setup-python: 'false' - - - name: generate cmake - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B _build - - - name: build cmake - run: | - cmake --build _build --config ${{ matrix.build_configuration }} --target package - sudo cmake --install _build - - - name: run ctest - run: | - ctest --test-dir _build --output-on-failure -C "${{ matrix.build_configuration }}" + - name: Install packages via apt + run: | + sudo apt update -qq && sudo apt install -y cmake pkg-config libssl-dev libudev-dev libhttp-parser-dev libpcsclite-dev \ + libgl1-mesa-dev qt6-l10n-tools + + - name: Install Qt + uses: jurplel/install-qt-action@v4 + with: + version: '6.8.1' + modules: 'qtscxml qtwebsockets qtshadertools qtconnectivity' + setup-python: 'false' + + - name: generate cmake + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B _build + + - name: build cmake + run: | + cmake --build _build --config ${{ matrix.build_configuration }} --target package + sudo cmake --install _build + + - name: run ctest + run: | + ctest --test-dir _build --output-on-failure -C "${{ matrix.build_configuration }}" build_macos: @@ -92,26 +93,26 @@ build_platform: ["Unix Makefiles"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - name: Install Qt - uses: jurplel/install-qt-action@v4 - with: - version: '6.8.*' - modules: 'qtscxml qtwebsockets qtshadertools qtconnectivity qtimageformats' - - - name: generate cmake - run: | - export OPENSSL_ROOT=/usr/local/opt/openssl/bin - export LDFLAGS=-L/usr/local/opt/openssl/lib - export CPPFLAGS=-I/usr/local/opt/openssl/include - export PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig/ - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B _build - - - name: build cmake - run: | - cmake --build _build --config ${{ matrix.build_configuration }} - - - name: run ctest - run: | - ctest --test-dir _build --output-on-failure -C "${{ matrix.build_configuration }}" + - name: Install Qt + uses: jurplel/install-qt-action@v4 + with: + version: '6.8.*' + modules: 'qtscxml qtwebsockets qtshadertools qtconnectivity qtimageformats' + + - name: generate cmake + run: | + export OPENSSL_ROOT=/usr/local/opt/openssl/bin + export LDFLAGS=-L/usr/local/opt/openssl/lib + export CPPFLAGS=-I/usr/local/opt/openssl/include + export PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig/ + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B _build + + - name: build cmake + run: | + cmake --build _build --config ${{ matrix.build_configuration }} + + - name: run ctest + run: | + ctest --test-dir _build --output-on-failure -C "${{ matrix.build_configuration }}" diff -Nru ausweisapp2-2.3.1/.github/workflows/CI_build_combined.yml ausweisapp2-2.4.0/.github/workflows/CI_build_combined.yml --- ausweisapp2-2.3.1/.github/workflows/CI_build_combined.yml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/.github/workflows/CI_build_combined.yml 2025-10-30 10:10:48.000000000 +0000 @@ -1,6 +1,6 @@ name: CI_build_combined -on: [push, pull_request] +"on": [push, pull_request] env: BUILD_DIR_LIBS_WIN: "c:/_build_libs" @@ -8,7 +8,6 @@ BUILD_DIR_LIBS: "_build_libs" BUILD_DIR_APP: "_build" - jobs: build_windows: @@ -21,38 +20,38 @@ steps: - - name: Install nmake replacement jom, ninja - run: | - choco install jom ninja - - - name: Checkout repo - uses: actions/checkout@v4 - - - name: Add nmake - uses: ilammy/msvc-dev-cmd@v1 - - - name: generate cmake libs - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B "${{ env.BUILD_DIR_LIBS_WIN }}" D:\a\AusweisApp\AusweisApp\libs - - - name: build cmake libs - run: | - cmake --build "${{ env.BUILD_DIR_LIBS_WIN }}" --config ${{ matrix.build_configuration }} - - - name: generate cmake - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B "${{ env.BUILD_DIR_APP_WIN }}" -DCMAKE_PREFIX_PATH=c:\_build_libs\dist D:\a\AusweisApp\AusweisApp - - - name: build cmake - run: | - cmake --build "${{ env.BUILD_DIR_APP_WIN }}" --config ${{ matrix.build_configuration }} --target package - cmake --install "${{ env.BUILD_DIR_APP_WIN }}" - - - name: run ctest - run: | - ctest --test-dir "${{ env.BUILD_DIR_APP_WIN }}" --output-on-failure -C "${{ matrix.build_configuration }}" - - + - name: Install nmake replacement jom, ninja + run: | + choco install jom ninja + + - name: Checkout repo + uses: actions/checkout@v5 + + - name: Add nmake + uses: ilammy/msvc-dev-cmd@v1 + + - name: generate cmake libs + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B "${{ env.BUILD_DIR_LIBS_WIN }}" \ + D:\a\AusweisApp\AusweisApp\libs + + - name: build cmake libs + run: | + cmake --build "${{ env.BUILD_DIR_LIBS_WIN }}" --config ${{ matrix.build_configuration }} + + - name: generate cmake + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B "${{ env.BUILD_DIR_APP_WIN }}" \ + -DCMAKE_PREFIX_PATH=c:\_build_libs\dist D:\a\AusweisApp\AusweisApp + + - name: build cmake + run: | + cmake --build "${{ env.BUILD_DIR_APP_WIN }}" --config ${{ matrix.build_configuration }} --target package + cmake --install "${{ env.BUILD_DIR_APP_WIN }}" + + - name: run ctest + run: | + ctest --test-dir "${{ env.BUILD_DIR_APP_WIN }}" --output-on-failure -C "${{ matrix.build_configuration }}" build_linux: @@ -64,33 +63,34 @@ build_platform: ["Ninja"] steps: - - uses: actions/checkout@v4 - - - name: Install packages via apt - run: | - sudo apt-get update -qq && sudo apt install -y cmake pkg-config libssl-dev libudev-dev libhttp-parser-dev libpcsclite-dev libgl1-mesa-dev libdbus-1-dev libclang-16-dev libclang-17-dev libclang-18-dev ninja-build - - - name: generate cmake libs - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B ${{ env.BUILD_DIR_LIBS }} ./libs - - - name: build cmake libs - run: | - cmake --build ${{ env.BUILD_DIR_LIBS }} --config ${{ matrix.build_configuration }} - - - name: generate cmake - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B ${{ env.BUILD_DIR_APP }} -DCMAKE_PREFIX_PATH=./_build_libs/dist - - - name: build cmake - run: | - cmake --build ${{ env.BUILD_DIR_APP }} --config ${{ matrix.build_configuration }} - sudo cmake --install ${{ env.BUILD_DIR_APP }} - - - name: run ctest - run: | - ctest --test-dir ${{ env.BUILD_DIR_APP }} --output-on-failure -C "${{ matrix.build_configuration }}" + - uses: actions/checkout@v5 + - name: Install packages via apt + run: | + sudo apt-get update -qq && sudo apt install -y cmake pkg-config libssl-dev libudev-dev libhttp-parser-dev libpcsclite-dev \ + libgl1-mesa-dev libdbus-1-dev libclang-16-dev libclang-17-dev libclang-18-dev ninja-build + + - name: generate cmake libs + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B ${{ env.BUILD_DIR_LIBS }} ./libs + + - name: build cmake libs + run: | + cmake --build ${{ env.BUILD_DIR_LIBS }} --config ${{ matrix.build_configuration }} + + - name: generate cmake + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B ${{ env.BUILD_DIR_APP }} \ + -DCMAKE_PREFIX_PATH=./_build_libs/dist + + - name: build cmake + run: | + cmake --build ${{ env.BUILD_DIR_APP }} --config ${{ matrix.build_configuration }} + sudo cmake --install ${{ env.BUILD_DIR_APP }} + + - name: run ctest + run: | + ctest --test-dir ${{ env.BUILD_DIR_APP }} --output-on-failure -C "${{ matrix.build_configuration }}" build_linux_android: @@ -102,34 +102,38 @@ build_platform: ["Unix Makefiles"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - name: Install packages via apt - run: | - sudo apt-get update -qq && sudo apt install -y cmake pkg-config libssl-dev libudev-dev libhttp-parser-dev libpcsclite-dev libgl1-mesa-dev libdbus-1-dev libclang-16-dev libclang-17-dev libclang-18-dev ninja-build - sudo apt -y remove firefox microsoft-edge-stable google-chrome-stable kotlin libmono* mono-runtime - - - name: generate cmake libs - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -DCMAKE_TOOLCHAIN_FILE=../cmake/android.toolchain.cmake -B ${{ env.BUILD_DIR_LIBS }} ./libs - - - name: build cmake libs - run: | - cmake --build ${{ env.BUILD_DIR_LIBS }} --config ${{ matrix.build_configuration }} - cmake --install ${{ env.BUILD_DIR_LIBS }} - - - name: generate cmake - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -DCMAKE_PREFIX_PATH=/home/runner/work/AusweisApp/AusweisApp/_build_libs/dist -DCMAKE_TOOLCHAIN_FILE=../cmake/android.toolchain.cmake -B ${{ env.BUILD_DIR_APP }} - - - name: build cmake - run: | - cmake --build ${{ env.BUILD_DIR_APP }} --config ${{ matrix.build_configuration }} - cmake --install ${{ env.BUILD_DIR_APP }} - - - name: run ctest - run: | - ctest --test-dir ${{ env.BUILD_DIR_APP }} --output-on-failure -C "${{ matrix.build_configuration }}" + - name: Install packages via apt + run: | + sudo apt-get update -qq && sudo apt install -y cmake pkg-config libssl-dev libudev-dev libhttp-parser-dev libpcsclite-dev \ + libgl1-mesa-dev libdbus-1-dev libclang-16-dev libclang-17-dev libclang-18-dev ninja-build + sudo apt -y remove firefox microsoft-edge-stable google-chrome-stable kotlin libmono* mono-runtime + + - name: generate cmake libs + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" \ + -DCMAKE_TOOLCHAIN_FILE=../cmake/android.toolchain.cmake -B ${{ env.BUILD_DIR_LIBS }} ./libs + + - name: build cmake libs + run: | + cmake --build ${{ env.BUILD_DIR_LIBS }} --config ${{ matrix.build_configuration }} + cmake --install ${{ env.BUILD_DIR_LIBS }} + + - name: generate cmake + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" \ + -DCMAKE_PREFIX_PATH=/home/runner/work/AusweisApp/AusweisApp/_build_libs/dist \ + -DCMAKE_TOOLCHAIN_FILE=../cmake/android.toolchain.cmake -B ${{ env.BUILD_DIR_APP }} + + - name: build cmake + run: | + cmake --build ${{ env.BUILD_DIR_APP }} --config ${{ matrix.build_configuration }} + cmake --install ${{ env.BUILD_DIR_APP }} + + - name: run ctest + run: | + ctest --test-dir ${{ env.BUILD_DIR_APP }} --output-on-failure -C "${{ matrix.build_configuration }}" build_macos: @@ -141,32 +145,33 @@ build_platform: ["Ninja"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - name: install ninja - run: | - brew install ninja - - - name: generate cmake libs - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B ${{ env.BUILD_DIR_LIBS }} ./libs - - - name: build cmake libs - run: | - cmake --build ${{ env.BUILD_DIR_LIBS }} --config ${{ matrix.build_configuration }} - - - name: generate cmake - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -DCMAKE_PREFIX_PATH=./_build_libs/dist -B ${{ env.BUILD_DIR_APP }} - - - name: build cmake - run: | - cmake --build ${{ env.BUILD_DIR_APP }} --config ${{ matrix.build_configuration }} - sudo cmake --install ${{ env.BUILD_DIR_APP }} - - - name: run ctest - run: | - ctest --test-dir ${{ env.BUILD_DIR_APP }} --output-on-failure -C "${{ matrix.build_configuration }}" + - name: install ninja + run: | + brew install ninja + + - name: generate cmake libs + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -B ${{ env.BUILD_DIR_LIBS }} ./libs + + - name: build cmake libs + run: | + cmake --build ${{ env.BUILD_DIR_LIBS }} --config ${{ matrix.build_configuration }} + + - name: generate cmake + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" \ + -DCMAKE_PREFIX_PATH=./_build_libs/dist -B ${{ env.BUILD_DIR_APP }} + + - name: build cmake + run: | + cmake --build ${{ env.BUILD_DIR_APP }} --config ${{ matrix.build_configuration }} + sudo cmake --install ${{ env.BUILD_DIR_APP }} + + - name: run ctest + run: | + ctest --test-dir ${{ env.BUILD_DIR_APP }} --output-on-failure -C "${{ matrix.build_configuration }}" build_ios: @@ -178,29 +183,32 @@ build_platform: ["Unix Makefiles"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - - name: install ninja - run: | - brew install ninja - - - name: generate cmake libs - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -DCMAKE_TOOLCHAIN_FILE=../cmake/iOS.toolchain.cmake -B ${{ env.BUILD_DIR_LIBS }} ./libs - - - name: build cmake libs - run: | - cmake --build ${{ env.BUILD_DIR_LIBS }} --config ${{ matrix.build_configuration }} - - - name: generate cmake - run: | - cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" -DCMAKE_PREFIX_PATH=/Users/runner/work/AusweisApp/AusweisApp/_build_libs/dist -DCMAKE_TOOLCHAIN_FILE=../cmake/iOS.toolchain.cmake -B ${{ env.BUILD_DIR_APP }} - - - name: build cmake - run: | - cmake --build ${{ env.BUILD_DIR_APP }} --config ${{ matrix.build_configuration }} - sudo cmake --install ${{ env.BUILD_DIR_APP }} - - - name: run ctest - run: | - ctest --test-dir ${{ env.BUILD_DIR_APP }} --output-on-failure -C "${{ matrix.build_configuration }}" + - name: install ninja + run: | + brew install ninja + + - name: generate cmake libs + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" \ + -DCMAKE_TOOLCHAIN_FILE=../cmake/iOS.toolchain.cmake -B ${{ env.BUILD_DIR_LIBS }} ./libs + + - name: build cmake libs + run: | + cmake --build ${{ env.BUILD_DIR_LIBS }} --config ${{ matrix.build_configuration }} + + - name: generate cmake + run: | + cmake -G "${{ matrix.build_platform }}" -DCMAKE_BUILD_TYPE="${{ matrix.build_configuration }}" \ + -DCMAKE_PREFIX_PATH=/Users/runner/work/AusweisApp/AusweisApp/_build_libs/dist \ + -DCMAKE_TOOLCHAIN_FILE=../cmake/iOS.toolchain.cmake -B ${{ env.BUILD_DIR_APP }} + + - name: build cmake + run: | + cmake --build ${{ env.BUILD_DIR_APP }} --config ${{ matrix.build_configuration }} + sudo cmake --install ${{ env.BUILD_DIR_APP }} + + - name: run ctest + run: | + ctest --test-dir ${{ env.BUILD_DIR_APP }} --output-on-failure -C "${{ matrix.build_configuration }}" diff -Nru ausweisapp2-2.3.1/.github/workflows/codeql.yml ausweisapp2-2.4.0/.github/workflows/codeql.yml --- ausweisapp2-2.3.1/.github/workflows/codeql.yml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/.github/workflows/codeql.yml 2025-10-30 10:10:48.000000000 +0000 @@ -1,10 +1,10 @@ name: "CodeQL" -on: +"on": push: - branches: [ "community" ] + branches: ["community"] pull_request: - branches: [ "community" ] + branches: ["community"] schedule: - cron: "46 18 * * 5" @@ -20,11 +20,11 @@ strategy: fail-fast: false matrix: - language: [ cpp ] + language: [cpp] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Initialize CodeQL uses: github/codeql-action/init@v3 @@ -33,9 +33,14 @@ queries: +security-and-quality - name: Install dependencies - run: sudo apt update -qq && sudo apt install -y cmake pkg-config libssl-dev libudev-dev libhttp-parser-dev libpcsclite-dev libqt6svg6-dev libqt6websockets6-dev qt6-base-dev qt6-base-private-dev qt6-declarative-dev qt6-connectivity-dev qt6-scxml-dev qt6-tools-dev qt6-tools-dev-tools libqt6opengl6-dev libqt6shadertools6-dev libgl1-mesa-dev qt6-l10n-tools + run: | + sudo apt update -qq && sudo apt install -y cmake pkg-config libssl-dev libudev-dev libhttp-parser-dev libpcsclite-dev \ + libqt6svg6-dev libqt6websockets6-dev qt6-base-dev qt6-base-private-dev \ + qt6-declarative-dev qt6-connectivity-dev qt6-scxml-dev qt6-tools-dev \ + qt6-tools-dev-tools libqt6opengl6-dev libqt6shadertools6-dev \ + libgl1-mesa-dev qt6-l10n-tools - #QT > 6.4 is required but ubuntu 22.04 just has 6.2.4, so additional installation is needed + # QT > 6.4 is required but ubuntu 22.04 just has 6.2.4, so additional installation is needed - name: Install Qt uses: jurplel/install-qt-action@v4 with: diff -Nru ausweisapp2-2.3.1/.gitlab-ci-child.yml ausweisapp2-2.4.0/.gitlab-ci-child.yml --- ausweisapp2-2.3.1/.gitlab-ci-child.yml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/.gitlab-ci-child.yml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,797 @@ +image: ${DOCKER_REGISTRY_MIRROR}/alpine:3.22 + +variables: + GIT_STRATEGY: empty + BINARY_DIR: $CI_PROJECT_DIR/bin + VIRTUAL_ENV: $CI_PROJECT_DIR/venv + CCACHE_BASEDIR: "$CI_PROJECT_DIR" + CCACHE_REMOTE_ONLY: true + CCACHE_RESHARE: true + +workflow: + name: '$PIPELINE_NAME' + +.prepare: + setup: + - env + - !reference [.prepare, pkg] + - !reference [.prepare, uv] + - hg clone -U ${REPOSITORY} source + - hg -R source update -r $REVISION + pkg: + - | + if command -v apk >/dev/null 2>&1; then + apk add mercurial cmake samurai make gdb lldb uv python3 + elif command -v apt-get >/dev/null 2>&1; then + apt-get update + apt-get install -y mercurial cmake ninja-build make gdb lldb python3 curl ca-certificates + curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.9.3/uv-installer.sh | sh + source $HOME/.local/bin/env + elif command -v dnf >/dev/null 2>&1; then + dnf -y install mercurial cmake ninja-build make gdb lldb uv python3 + elif command -v sw_vers >/dev/null 2>&1; then + export UV_NO_MODIFY_PATH=1 + export UV_INSTALL_DIR=$BINARY_DIR + curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.9.3/uv-installer.sh | sh + curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ccache/ccache/releases/download/v4.12.1/ccache-4.12.1-darwin.tar.gz \ + | tar -C $BINARY_DIR --strip-components=1 -xz "*/ccache" + export PATH="$BINARY_DIR/:$PATH" + fi + uv: + - uv venv $VIRTUAL_ENV + - source $VIRTUAL_ENV/bin/activate + - | + if command -v sw_vers >/dev/null 2>&1; then + uv pip install mercurial cmake==4.1.0 ninja + fi + +.Base: + tags: + - $LOW_RUNNER + rules: + - if: '$SELECT_JOB_REGEX != null && $CI_JOB_NAME !~ $SELECT_JOB_REGEX && ($CI_JOB_NAME =~ /^Libraries/ || $SELECT_JOB_MANUAL == null)' + when: never + - if: '$USE_DISTRIBUTION_PROFILE == "ON" && $RELEASE != "true"' + when: never + - if: '$SELECT_JOB_REGEX != null && $CI_JOB_NAME !~ $SELECT_JOB_REGEX' + when: manual + - if: '$CI_JOB_NAME =~ /^Deploy_Final/ && $RELEASE_FINAL != "true"' + when: never + - if: '$CI_JOB_NAME =~ /^Deploy/ && $RELEASE == "true"' + when: manual + - if: '$CI_JOB_NAME =~ /^Deploy/' + when: never + - if: '$CI_JOB_NAME =~ /^Light/ && $SELECT_JOB_REGEX != null && $CI_JOB_NAME =~ $SELECT_JOB_REGEX' + when: on_success + - if: '$CI_JOB_NAME =~ /^Light/' + when: never + - when: on_success + +.Packages: + extends: .Base + variables: + PACKAGES_DIR: $CI_PROJECT_DIR/packages + cache: + - key: packages + paths: + - packages + when: always + +default: + before_script: + - export PATH=$BINARY_DIR:$PATH + - !reference [.prepare, setup] + +Libraries_Linux: + extends: .Packages + tags: + - $HIGH_RUNNER + before_script: + - !reference [.prepare, setup] + - | + apk add wget g++ ccache perl \ + pkgconf lld pcsc-lite-dev \ + mesa-dev libx11-dev libxkbcommon-dev xcb-util-wm-dev xcb-util-image-dev xcb-util-keysyms-dev + script: + - cmake -P source/ci.cmake + +Linux_Debian_Vanilla: + extends: .Base + tags: + - $MEDIUM_RUNNER + parallel: + matrix: + - version: + - trixie-slim + image: ${DOCKER_REGISTRY_MIRROR}/debian:$version + variables: + XDG_RUNTIME_DIR: "$CI_PROJECT_DIR/xdg" + LANG: "C.UTF-8" + LC_ALL: "C.UTF-8" + script: + - mkdir -p "$XDG_RUNTIME_DIR" + - chmod 700 "$XDG_RUNTIME_DIR" + - | + apt-get install -y g++ pkgconf ccache \ + libhttp-parser-dev libpcsclite-dev libssl-dev \ + qt6-base-dev qt6-base-private-dev qt6-declarative-dev \ + qt6-scxml-dev qt6-svg-dev \ + qt6-tools-dev qt6-websockets-dev qt6-connectivity-dev \ + \ + libqt6svg6 qml6-module-qt-labs-platform qml6-module-qtqml \ + qml6-module-qtqml-models qml6-module-qtqml-statemachine \ + qml6-module-qtqml-workerscript qml6-module-qtquick-controls \ + qml6-module-qtquick-layouts qml6-module-qtquick-templates \ + qml6-module-qtquick-window qml6-module-qtquick-dialogs \ + qml6-module-qtcore + + if [ "$version" == "bookworm-slim" ]; then + apt-get install -y qml6-module-qt-labs-folderlistmodel + fi + + - cmake -P source/ci.cmake + artifacts: + expire_in: 1 week + reports: + junit: build/**/results.*.junit.xml + +Linux_Alpine: + extends: Libraries_Linux + tags: + - $MEDIUM_RUNNER + needs: + - job: Libraries_Linux + artifacts: false + optional: true + parallel: + matrix: + - variant: [App, Integrated] + compiler: [g++, clang++, clazy] + cxx_standard: [20] + - variant: [App] + compiler: [g++] + cxx_standard: [17] + script: + - apk add clang clazy gnupg gcovr check-jsonschema typos py3-codespell yamllint ruff + - uv pip install sphinx-lint + - cmake -P source/ci.cmake -- -DCMAKE_CXX_COMPILER=${compiler} -DCMAKE_CXX_STANDARD=${cxx_standard} + artifacts: + expire_in: 1 week + reports: + junit: build/**/results.*.junit.xml + +Translations: + extends: Libraries_Linux + tags: + - $LOW_RUNNER + needs: + - job: Libraries_Linux + artifacts: false + optional: true + script: + - cmake -P source/ci.cmake + artifacts: + expire_in: 1 week + paths: + - source/resources/translations/*.ts + +Linux_OpenSSL: + extends: Libraries_Linux + tags: + - $HIGH_RUNNER + parallel: + matrix: + - openssl: + - "1.1.1w" + - "3.0.18" + script: + - apk add gcovr + - cmake -DOPENSSL=${openssl} -P source/ci.cmake + artifacts: + expire_in: 1 week + reports: + junit: build/**/results.*.junit.xml + +SonarQube: + extends: .Packages + tags: + - $HIGH_RUNNER + image: ${DOCKER_REGISTRY_MIRROR}/ubuntu:25.10 + variables: + LANG: "C.UTF-8" + LC_ALL: "C.UTF-8" + cache: + - key: packages + paths: + - packages + when: always + - key: "${CI_JOB_NAME}-${REV}" + paths: + - sonar + when: always + script: + - | + apt-get install -y openjdk-21-jdk-headless g++ pkgconf ccache mold \ + libpcsclite-dev perl libgl1-mesa-dev libglu1-mesa-dev libegl1-mesa-dev + - uv pip install gcovr + - cmake -DSPLIT=OFF -P source/ci.cmake + artifacts: + expire_in: 1 week + reports: + junit: build/**/results.*.junit.xml + +Light_Analyze: + extends: SonarQube + resource_group: SonarQube + +Formatting: + extends: Libraries_Linux + tags: + - $LOW_RUNNER + needs: + - job: Libraries_Linux + artifacts: false + optional: true + script: + - apk add dos2unix uncrustify ruff yamlfmt + - cmake -P source/ci.cmake | tee output.log + - if grep -q "FORMATTING FAILED" output.log; then exit 1; fi + +Linux_Debian_Qt: + extends: .Base + tags: + - $MEDIUM_RUNNER + parallel: + matrix: + - QT: + - 6.8.0 + - 6.9.0 + - 6.10.0 + image: ${DOCKER_REGISTRY_MIRROR}/debian:stable-slim + variables: + LANG: "C.UTF-8" + LC_ALL: "C.UTF-8" + script: + - | + apt-get install -y g++ pkgconf ccache libpcsclite-dev libssl-dev libudev-dev \ + libglib2.0-0 libvulkan-dev libegl1-mesa-dev libxkbcommon0 libdbus-1-3 \ + libfontconfig1 + - uv pip install aqtinstall + - cmake -DQT=$QT -P source/ci.cmake + artifacts: + expire_in: 1 week + reports: + junit: build/**/results.*.junit.xml + +.AndroidBase: + extends: .Packages + variables: + ANDROID_CMDLINE_TOOLS: 13114758 + ANDROID_NDK_VERSION: 28.2.13676358 + ANDROID_COMPONENTS: '"cmdline-tools;19.0" "build-tools;36.1.0" "platforms;android-35" "ndk;${ANDROID_NDK_VERSION}"' + ANDROID_SDK_ROOT: $CI_PROJECT_DIR/android-sdk + ANDROID_NDK_ROOT: $ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION + ANDROID_CMDLINE_TOOLS_ZIP: commandlinetools-linux-${ANDROID_CMDLINE_TOOLS}_latest.zip + GRADLE_USER_HOME: $CI_PROJECT_DIR/.gradle + cache: + - key: packages + paths: + - packages + when: always + - key: "${CI_JOB_NAME}-gradle" + paths: + - .gradle/caches/ + - .gradle/wrapper/ + - key: "${CI_JOB_NAME}-${ANDROID_CMDLINE_TOOLS}-${ANDROID_NDK_VERSION}-${ANDROID_COMPONENTS}" + paths: + - $ANDROID_SDK_ROOT + before_script: + - !reference [.prepare, setup] + - apk add gcompat maven gradle ccache unzip perl bash g++ linux-headers + - | + if [ ! -d "$ANDROID_SDK_ROOT" ]; then + if [ ! -f "$PACKAGES_DIR/$ANDROID_CMDLINE_TOOLS_ZIP" ]; then + mkdir -p $PACKAGES_DIR + wget -O $PACKAGES_DIR/$ANDROID_CMDLINE_TOOLS_ZIP "https://dl.google.com/android/repository/$ANDROID_CMDLINE_TOOLS_ZIP" + fi + unzip -d /tmp/cmd $PACKAGES_DIR/$ANDROID_CMDLINE_TOOLS_ZIP + set +o pipefail + yes | /tmp/cmd/cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT $(eval echo $ANDROID_COMPONENTS) + rm -rf /tmp/cmd + set -o pipefail + fi + artifacts: + expire_in: 1 week + paths: + - build/dist/** + - build/src/libAusweisApp* + +Libraries_Android: + extends: .AndroidBase + tags: + - $HIGH_RUNNER + parallel: + matrix: + - arch: arm64-v8a + - arch: armeabi-v7a + - arch: x86_64 + script: + - cmake -P source/ci.cmake -- -DCMAKE_ANDROID_ARCH_ABI=${arch} + +Android_AAR: + extends: .AndroidBase + tags: + - $MEDIUM_RUNNER + needs: + - job: Libraries_Android + artifacts: false + optional: true + parallel: + matrix: + - arch: + - arm64-v8a + - armeabi-v7a + - x86_64 + script: + - cmake -DPROPAGATE=ON -P source/ci.cmake -- -DCMAKE_ANDROID_ARCH_ABI=${arch} + +Android_Merged-AAR: + extends: .Base + tags: + - $LOW_RUNNER + needs: + - job: Android_AAR + artifacts: true + script: + - apk add maven gnupg + - find build/dist -type f -exec mv {} $CI_PROJECT_DIR/ \; + - cmake -P source/ci.cmake + artifacts: + expire_in: 1 week + paths: + - build/dist/** + +Android_APK: + extends: .AndroidBase + tags: + - $MEDIUM_RUNNER + needs: + - job: Libraries_Android + artifacts: false + optional: true + parallel: + matrix: + - arch: + - arm64-v8a + - armeabi-v7a + - x86_64 + script: + - | + if [ "$RELEASE" == "true" ]; then + uv pip install google-api-python-client + fi + - cmake -DPROPAGATE=ON -P source/ci.cmake -- -DCMAKE_ANDROID_ARCH_ABI=${arch} + +Docs: + image: + name: ${DOCKER_REGISTRY_MIRROR}/pandoc/latex:3.6.4-alpine + entrypoint: [""] + extends: .Base + script: + - | + apk add py3-setuptools icu poppler zziplib enscript ghostscript \ + py3-sphinx py3-sphinx_rtd_theme py3-sphinx-copybutton py3-sphinxcontrib-tabs + - uv pip install doc8 + - | + tlmgr --repository=https://ftp.tu-chemnitz.de/pub/tug/historic/systems/texlive/2024/tlnet-final install \ + latexmk cmap fncychap float wrapfig capt-of framed upquote needspace tabulary varwidth parskip titlesec \ + pdfmanagement-testphase tagpdf xcolor sectsty pdflscape fancyvrb tcolorbox booktabs mdwtools caption \ + babel-german csquotes tex-gyre l3experimental + - cmake -P source/ci.cmake + artifacts: + expire_in: 1 week + paths: + - build/*-Lizenz.pdf + - build/*-Lizenz.txt + - build/docs/**/*.pdf + - build/docs/**/*.tar.xz + - build/docs/notes/singlehtml/**/appcast.html + - build/docs/**/*.inv + +Source: + extends: .Base + script: + - apk add gnupg + - cmake -P source/ci.cmake + artifacts: + expire_in: 1 week + paths: + - build/*.tar.gz* + +Container: + extends: .Base + tags: + - $CONTAINER_RUNNER + parallel: + matrix: + - target: + - SDK + - VNC + variables: + STORAGE_DRIVER: vfs + script: + - apk add buildah runc netavark iptables + - | + cat < /etc/containers/registries.conf.d/mirror.conf + + [[registry]] + location = "docker.io" + mirror-by-digest-only = false + + [[registry.mirror]] + location = "${DOCKER_REGISTRY_MIRROR}" + EOF + - cmake -P source/ci.cmake + artifacts: + expire_in: 1 week + paths: + - build/AusweisApp*.tar + +FreeBSD: + extends: .Packages + tags: + - freebsd + script: + - unset CCACHE_REMOTE_STORAGE + - unset CCACHE_REMOTE_ONLY + - cmake -P source/ci.cmake + artifacts: + expire_in: 1 week + reports: + junit: build/**/results.*.junit.xml + +Libraries_iOS: + extends: .Packages + parallel: + matrix: + - target: + - OS + - Simulator_x86_64 + - Simulator_arm64 + tags: + - iOS + script: + - cmake -P source/ci.cmake + +.iOS: + extends: .Packages + tags: + - iOS + script: + - cmake -P source/ci.cmake + artifacts: + expire_in: 1 week + paths: + - build/*.ipa + - build/*.zip + - build/*.bcsymbolmap + - AusweisApp_BuildDir.tar.zstd + +iOS_IPA: + extends: .iOS + needs: + - job: Libraries_iOS + artifacts: false + optional: true + parallel: + matrix: + - target: OS + parallel: + matrix: + - USE_DISTRIBUTION_PROFILE: + - 'ON' + - 'OFF' + script: + - cmake -P source/ci.cmake -- -DUSE_DISTRIBUTION_PROFILE=${USE_DISTRIBUTION_PROFILE} + +iOS_Framework: + extends: .iOS + needs: + - job: Libraries_iOS + artifacts: false + optional: true + parallel: + matrix: + - target: + - OS + - Simulator_x86_64 + - Simulator_arm64 + +iOS_SwiftPackage: + extends: .iOS + needs: + - job: iOS_Framework + artifacts: true + script: + - find build -type f -exec mv {} $CI_PROJECT_DIR/ \; + - cmake -P source/ci.cmake + artifacts: + expire_in: 1 week + paths: + - build/dist/*.zip + +Libraries_MacOS: + extends: .Packages + tags: + - macOS + script: + - cmake -P source/ci.cmake + +MacOS: + extends: Libraries_MacOS + needs: + - job: Libraries_MacOS + artifacts: false + optional: true + parallel: + matrix: + - target: DMG_PKG + USE_DISTRIBUTION_PROFILE: 'ON' + - target: DMG_PKG + USE_DISTRIBUTION_PROFILE: 'OFF' + - target: Tests + - target: Integrated + script: + - cmake -P source/ci.cmake -- -DUSE_DISTRIBUTION_PROFILE=${USE_DISTRIBUTION_PROFILE} + artifacts: + expire_in: 1 week + paths: + - build/*.dmg* + - build/*.pkg* + reports: + junit: build/**/results.*.junit.xml + +.Windows: + extends: .Packages + variables: + GIT_STRATEGY: none + before_script: + - "ls env:" + - | + $items = Get-ChildItem -Force + foreach ($item in $items) { + if ($item.PSIsContainer -and $item.Name -ieq "packages") { + continue + } + cmake -E rm -rf $item.name + } + - | + hg clone -U $REPOSITORY source + hg update -r $REVISION -R source + - uv venv $VIRTUAL_ENV + - powershell -Command "& '$env:VIRTUAL_ENV\Scripts\activate.ps1'" + script: + - cmake -P source/ci.cmake + after_script: + - cmake -E rm -rf source libs + artifacts: + expire_in: 1 week + paths: + - build/*.msi* + - build/Appcast* + reports: + junit: build/**/results.*.junit.xml + +Libraries_Win_GNU: + extends: .Windows + tags: + - Win64_GNU + +Libraries_Win_MSVC: + extends: .Windows + tags: + - Win64_MSVC + parallel: + matrix: + - target: + - release + - dev + +Win_GNU: + extends: Libraries_Win_GNU + needs: + - job: Libraries_Win_GNU + artifacts: false + optional: true + parallel: + matrix: + - target: + - MSI + - Tests + +Win_MSVC: + extends: Libraries_Win_MSVC + needs: + - job: Libraries_Win_MSVC + artifacts: false + optional: true + parallel: + matrix: + - target: MSI + compiler: cl + - target: MSI + compiler: clang-cl + - target: MSI + compiler: clang++ + - target: MSI_dev + compiler: cl + - target: Tests_dev + compiler: cl + script: + - cmake -P source/ci.cmake -- -DCMAKE_CXX_COMPILER="$env:compiler" + +.Deploy: + extends: .Base + stage: deploy + script: + - find build -type f -exec mv {} $CI_PROJECT_DIR/ \; + - cmake -P source/ci.cmake + +Deploy_iOS_IPA: + extends: .Deploy + tags: + - iOS + needs: + - job: iOS_IPA + parallel: + matrix: + - USE_DISTRIBUTION_PROFILE: 'ON' + +Deploy_Final_Framework: + extends: .Deploy + needs: + - job: iOS_SwiftPackage + script: + - apk add github-cli + - !reference [.Deploy, script] + +Deploy_MacOS_PKG: + extends: .Deploy + tags: + - macOS + needs: + - job: MacOS + parallel: + matrix: + - target: DMG_PKG + USE_DISTRIBUTION_PROFILE: 'ON' + +Deploy_Android_APK: + extends: .Deploy + needs: + - job: Android_APK + script: + - uv pip install google-api-python-client + - !reference [.Deploy, script] + +Deploy_Android_AAR: + extends: .Deploy + needs: + - job: Android_Merged-AAR + script: + - apk add curl + - !reference [.Deploy, script] + +Deploy_Archive: + extends: .Deploy + needs: + - job: Android_APK + - job: Android_Merged-AAR + - job: iOS_SwiftPackage + - job: iOS_IPA + parallel: + matrix: + - USE_DISTRIBUTION_PROFILE: 'ON' + - USE_DISTRIBUTION_PROFILE: 'OFF' + - job: MacOS + parallel: + matrix: + - target: DMG_PKG + USE_DISTRIBUTION_PROFILE: 'ON' + - target: DMG_PKG + USE_DISTRIBUTION_PROFILE: 'OFF' + - job: Win_MSVC + parallel: + matrix: + - target: MSI + compiler: cl + - job: Docs + - job: Source + script: + - apk add samba-client + - !reference [.Deploy, script] + +Deploy_Allowlist: + extends: .Deploy + needs: + - job: Win_MSVC + parallel: + matrix: + - target: MSI + compiler: cl + - job: MacOS + parallel: + matrix: + - target: DMG_PKG + USE_DISTRIBUTION_PROFILE: 'OFF' + script: + - apk add curl + - !reference [.Deploy, script] + +Deploy_Cloud: + extends: .Deploy + needs: + - job: Android_APK + - job: iOS_IPA + parallel: + matrix: + - USE_DISTRIBUTION_PROFILE: 'OFF' + - job: MacOS + parallel: + matrix: + - target: DMG_PKG + USE_DISTRIBUTION_PROFILE: 'OFF' + - job: Win_MSVC + parallel: + matrix: + - target: MSI + compiler: cl + script: + - apk add curl + - !reference [.Deploy, script] + +Deploy_Final_Container: + extends: .Deploy + needs: + - job: Container + parallel: + matrix: + - target: SDK + script: + - apk add skopeo + - !reference [.Deploy, script] + +Deploy_Final_GitHub: + extends: .Deploy + needs: + - job: Android_APK + - job: Source + - job: MacOS + parallel: + matrix: + - target: DMG_PKG + USE_DISTRIBUTION_PROFILE: 'OFF' + - job: Win_MSVC + parallel: + matrix: + - target: MSI + compiler: cl + - job: Docs + script: + - apk add github-cli + - !reference [.Deploy, script] + +Light_Configuration: + extends: Libraries_Linux + needs: + - job: Libraries_Linux + artifacts: false + optional: true + script: + - cmake -P source/ci.cmake + +Light_Packages: + extends: .Base + script: + - uv pip install python-gitlab + - cmake -P source/ci.cmake diff -Nru ausweisapp2-2.3.1/.gitlab-ci.yml ausweisapp2-2.4.0/.gitlab-ci.yml --- ausweisapp2-2.3.1/.gitlab-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/.gitlab-ci.yml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,68 @@ +spec: + inputs: + revision: + description: "Use revision of repository." + type: string + default: "default" + release: + description: "Indicates whether the pipeline is being used for a release." + type: boolean + default: false +--- + +image: ${DOCKER_REGISTRY_MIRROR}/alpine + +variables: + PIPELINE_NAME: '$CI_COMMIT_TITLE' + REPOSITORY: https://hg.governikus.de/AusweisApp/AusweisApp2 + +workflow: + name: '$PIPELINE_NAME' + rules: + - if: '$REVIEWBOARD_REVIEW_ID != null' + variables: + PIPELINE_NAME: '${REVIEWBOARD_REVIEW_ID} / ${REVIEWBOARD_DIFF_REVISION}' + - if: '$PATCH_IDENTIFIER != null' + variables: + PIPELINE_NAME: 'Rev: $[[ inputs.revision ]] | Patch: ${PATCH_IDENTIFIER}' + - if: '$SELECT_JOB_REGEX != null' + variables: + PIPELINE_NAME: 'Rev: $[[ inputs.revision ]] | ${SELECT_JOB_REGEX}' + - when: always + variables: + PIPELINE_NAME: 'Rev: $[[ inputs.revision ]]' + +stages: + - generator + - trigger + +Bootstrap: + stage: generator + tags: + - $BOOTSTRAP_RUNNER + variables: + VIRTUAL_ENV: $CI_PROJECT_DIR/venv + before_script: + - env + - apk add mercurial cmake uv + - uv venv $VIRTUAL_ENV + - source $VIRTUAL_ENV/bin/activate + script: + - hg clone -U ${REPOSITORY} source + - hg update -r $[[ inputs.revision ]] -R source + - cmake -DSPLIT=OFF -DRELEASE="$[[ inputs.release ]]" -DREV="$[[ inputs.revision ]]" -P source/ci.cmake + artifacts: + paths: + - gitlab-ci-child.yml + reports: + dotenv: env + +execute: + stage: trigger + trigger: + include: + - artifact: gitlab-ci-child.yml + job: Bootstrap + strategy: depend + forward: + pipeline_variables: true diff -Nru ausweisapp2-2.3.1/.qmlformat.ini ausweisapp2-2.4.0/.qmlformat.ini --- ausweisapp2-2.3.1/.qmlformat.ini 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/.qmlformat.ini 2025-10-30 10:10:48.000000000 +0000 @@ -1,5 +1,10 @@ [General] -UseTabs=true +FunctionsSpacing=false IndentWidth=4 +MaxColumnWidth=-1 NewlineType=unix NormalizeOrder=true +ObjectsSpacing=false +SemicolonRule=always +SortImports=false +UseTabs=true diff -Nru ausweisapp2-2.3.1/.qmllint.ini ausweisapp2-2.4.0/.qmllint.ini --- ausweisapp2-2.3.1/.qmllint.ini 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/.qmllint.ini 2025-10-30 10:10:48.000000000 +0000 @@ -38,7 +38,7 @@ AttachedPropertyType=warning ControlsAttachedPropertyReuse=warning ControlsNativeCustomize=warning -LayoutsPositioning=disable +LayoutsPositioning=warning PropertyChangesParsed=warning UnexpectedVarType=warning diff -Nru ausweisapp2-2.3.1/.typos.toml ausweisapp2-2.4.0/.typos.toml --- ausweisapp2-2.3.1/.typos.toml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/.typos.toml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,188 @@ +[files] +extend-exclude = [ + "CMakeLists.txt.user", + "LICENSE*", + "CONTRIBUTING.rst", + "README.rst", + "docs/**", + "src/external/**", + "resources/**", + "libs/**", + "test/fixture/**", +] +ignore-hidden = true +ignore-files = true +ignore-dot = true +ignore-vcs = true +ignore-global = true +ignore-parent = true + +[default] +binary = false +check-filename = true +check-file = true +unicode = true +ignore-hex = true +identifier-leading-digits = false +locale = "en" +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [ + '(?Rm)^.*(#|//)\s*codespell:ignore$', + "\\b(MDA|MII)[A-Za-z0-9]{61,}\\b", + "\\b[A-Za-z0-9+/]{60,}\\b", + "Governikus Root CA 4:PN", + "Governikus CA 9:PN", + "DEDV", + "mCvdv_DED", + "cvdv_DED", + "cvdv-DED", + "cvat-DED", + "Q_INVOKABLE", + "performTAandCA", + "tAandCAResult", + "TAandCAResult", + "HPE_", + "bund.de", + "AusweisApp Bund", + "-sice:", + '"_ba', + "itms-apps:", + "posix-seh", + "E-Mail-Adresse", + "AppUpdatData", + "test_AppUpdatr", + "-DPENDING_PATCH", +] + +[default.extend-identifiers] + +[default.extend-words] + +[type.lock] +extend-glob = [] +check-file = false +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.lock.extend-identifiers] + +[type.lock.extend-words] + +[type.man] +extend-glob = [] +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.man.extend-identifiers] +Nd = "Nd" + +[type.man.extend-words] + +[type.cpp] +extend-glob = [] +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.cpp.extend-identifiers] +countr_one = "countr_one" + +[type.cpp.extend-words] + +[type.rust] +extend-glob = [] +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.rust.extend-identifiers] +flate2 = "flate2" + +[type.rust.extend-words] +ser = "ser" + +[type.vimscript] +extend-glob = [] +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.vimscript.extend-identifiers] +windo = "windo" + +[type.vimscript.extend-words] + +[type.go] +extend-glob = [] +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.go.extend-identifiers] +flate = "flate" + +[type.go.extend-words] + +[type.py] +extend-glob = [] +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.py.extend-identifiers] +NDArray = "NDArray" +EOFError = "EOFError" +arange = "arange" + +[type.py.extend-words] + +[type.sh] +extend-glob = [] +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.sh.extend-identifiers] +ot = "ot" +stap = "stap" + +[type.sh.extend-words] + +[type.css] +extend-glob = [] +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.css.extend-identifiers] +nd = "nd" + +[type.css.extend-words] + +[type.jl] +extend-glob = [] +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.jl.extend-identifiers] + +[type.jl.extend-words] +egal = "egal" +usig = "usig" +modul = "modul" +egals = "egals" + +[type.cert] +extend-glob = [] +check-file = false +extend-ignore-identifiers-re = [] +extend-ignore-words-re = [] +extend-ignore-re = [] + +[type.cert.extend-identifiers] + +[type.cert.extend-words] diff -Nru ausweisapp2-2.3.1/.yamlfmt ausweisapp2-2.4.0/.yamlfmt --- ausweisapp2-2.3.1/.yamlfmt 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/.yamlfmt 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,7 @@ +formatter: + type: basic + indent: 2 + line_ending: lf + retain_line_breaks: true + retain_line_breaks_single: true + eof_newline: true diff -Nru ausweisapp2-2.3.1/.yamllint ausweisapp2-2.4.0/.yamllint --- ausweisapp2-2.3.1/.yamllint 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/.yamllint 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,6 @@ +extends: default + +rules: + document-start: disable + line-length: + max: 150 diff -Nru ausweisapp2-2.3.1/CMakeLists.txt ausweisapp2-2.4.0/CMakeLists.txt --- ausweisapp2-2.3.1/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -1,8 +1,13 @@ -cmake_minimum_required(VERSION 3.19.0) +cmake_minimum_required(VERSION 3.25) set(CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(CMAKE_MODULE_PATH "${CMAKE_DIR}") +option(PROVIDER "Enable dependency provider" OFF) +if(PROVIDER) + set(CMAKE_PROJECT_TOP_LEVEL_INCLUDES "${CMAKE_DIR}/Provider.cmake") +endif() + include(Policies NO_POLICY_SCOPE) # "tools.only" can be defined to disable the normal build and enable @@ -85,6 +90,18 @@ endif() message(STATUS "BUILD_PREVIEW: ${BUILD_PREVIEW}") + if(ANDROID_VERSION_CODE_BASE) + if(CMAKE_ANDROID_ARCH_ABI STREQUAL "armeabi-v7a") + set(COUNTER 0) + elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") + set(COUNTER 1) + elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64") + set(COUNTER 2) + else() + message(FATAL_ERROR "Unknown architecture: ${CMAKE_ANDROID_ARCH_ABI}") + endif() + math(EXPR ANDROID_VERSION_CODE "${ANDROID_VERSION_CODE_BASE} + ${COUNTER}") + endif() if(NOT ANDROID_VERSION_CODE) set(ANDROID_VERSION_CODE 0) endif() @@ -110,13 +127,14 @@ set(DOCS_DIR ${PROJECT_SOURCE_DIR}/docs) set(RESOURCES_DIR ${PROJECT_SOURCE_DIR}/resources) set(PACKAGING_DIR ${RESOURCES_DIR}/packaging) -set(COPYRIGHT_TEXT "2014-2024 ${VENDOR}") +set(COPYRIGHT_TEXT "2014-2025 ${VENDOR}") if(APPLE) string(REPLACE " \& " " \& " COPYRIGHT_TEXT ${COPYRIGHT_TEXT}) endif() set(BUNDLE_IDENTIFIER com.governikus.ausweisapp2) if(NOT CONTAINER_SDK) + set(VALGRIND_COMMAND_OPTIONS "--tool=memcheck --leak-check=full --show-leak-kinds=definite --errors-for-leak-kinds=definite --track-origins=yes --error-exitcode=1") include(CTest) endif() @@ -124,7 +142,6 @@ include(DVCS) add_subdirectory(docs) -include(Appcast) include(Messages) if(tools.only) include(Packaging) @@ -155,21 +172,11 @@ include(Packaging) if(BUILD_TESTING) - set(VALGRIND_SUPPRESSIONS "${CMAKE_PREFIX_PATH}/test/valgrind.supp") - if(NOT EXISTS "${VALGRIND_SUPPRESSIONS}") - set(VALGRIND_SUPPRESSIONS "${CMAKE_SOURCE_DIR}/libs/test/valgrind.supp") - endif() - message(STATUS "Using valgrind suppressions: ${VALGRIND_SUPPRESSIONS}") - set(VALGRIND_COMMAND_OPTIONS "--tool=memcheck --leak-check=full --show-leak-kinds=definite --errors-for-leak-kinds=definite --error-exitcode=1 --gen-suppressions=all --suppressions=${VALGRIND_SUPPRESSIONS}") - configure_file("${CMAKE_DIR}/CTestCustom.cmake.in" "${CMAKE_BINARY_DIR}/CTestCustom.cmake" @ONLY) - set(SONAR_CACHE_DIR ${CMAKE_BINARY_DIR}/../cache) - if(NOT EXISTS "${SONAR_CACHE_DIR}") - set(SONAR_CACHE_DIR ${CMAKE_BINARY_DIR}) + if(EXISTS "${PROJECT_SOURCE_DIR}/ci") + configure_file("${PROJECT_SOURCE_DIR}/ci/sonar-project.properties.in" "${CMAKE_BINARY_DIR}/sonar-project.properties" @ONLY) endif() - get_filename_component(SONAR_CACHE_DIR ${SONAR_CACHE_DIR} ABSOLUTE) - configure_file("${RESOURCES_DIR}/sonar-project.properties.in" "${CMAKE_BINARY_DIR}/sonar-project.properties" @ONLY) endif() include(FeatureSummary) diff -Nru ausweisapp2-2.3.1/CMakePresets.json ausweisapp2-2.4.0/CMakePresets.json --- ausweisapp2-2.3.1/CMakePresets.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/CMakePresets.json 2025-10-30 10:10:48.000000000 +0000 @@ -6,12 +6,12 @@ "patch": 0 }, "include": [ - "presets/ci-android.json", - "presets/ci-bsd.json", - "presets/ci-linux.json", - "presets/ci-macOS.json", - "presets/ci-iOS.json", - "presets/ci-tools.json", - "presets/ci-windows.json" + "ci/presets/android.json", + "ci/presets/bsd.json", + "ci/presets/linux.json", + "ci/presets/macOS.json", + "ci/presets/iOS.json", + "ci/presets/tools.json", + "ci/presets/windows.json" ] } diff -Nru ausweisapp2-2.3.1/CONTRIBUTING.rst ausweisapp2-2.4.0/CONTRIBUTING.rst --- ausweisapp2-2.3.1/CONTRIBUTING.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/CONTRIBUTING.rst 2025-10-30 10:10:48.000000000 +0000 @@ -27,7 +27,7 @@ Code Style ---------- -Wir verwenden `uncrustify` um den Quellcode automatisch zu formatieren. +Wir verwenden ``uncrustify`` um den Quellcode automatisch zu formatieren. Mittels CMake wird das Build-Target "format" im jeweiligen "Makefile" generiert, welches die Formatierung für alle Dateien vornimmt. diff -Nru ausweisapp2-2.3.1/Dockerfile ausweisapp2-2.4.0/Dockerfile --- ausweisapp2-2.3.1/Dockerfile 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/Dockerfile 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -ARG ALPINE_VERSION=3.21 +ARG ALPINE_VERSION=3.22 FROM alpine:$ALPINE_VERSION AS builder # Install development stuff diff -Nru ausweisapp2-2.3.1/Dockerfile.vnc ausweisapp2-2.4.0/Dockerfile.vnc --- ausweisapp2-2.3.1/Dockerfile.vnc 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/Dockerfile.vnc 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,60 @@ +ARG ALPINE_VERSION=3.22 + +FROM alpine:$ALPINE_VERSION AS builder +# Install development stuff +RUN apk --no-cache upgrade -a && \ + apk --no-cache add patch cmake ccache make ninja g++ pkgconf pcsc-lite-dev binutils-gold eudev-libs \ + http-parser-dev openssl-dev \ + qt6-qtbase-dev qt6-qtsvg-dev qt6-qtwebsockets-dev qt6-qttools-dev qt6-qtdeclarative-dev qt6-qtscxml-dev qt6-qtconnectivity-dev qt6-qtimageformats-dev qt6-qtimageformats + +# Use optional remote ccache +# redis://YOUR_SERVER:6379 +ARG CCACHE_REMOTE_STORAGE="" +ENV CCACHE_REMOTE_STORAGE=$CCACHE_REMOTE_STORAGE CCACHE_REMOTE_ONLY=true CCACHE_RESHARE=true CCACHE_DIR=/build/ccache XDG_RUNTIME_DIR=/root + +# Build AusweisApp +COPY README.rst /src/ausweisapp/ +COPY LICENSE.txt/ /src/ausweisapp/ +COPY LICENSE.officially.txt/ /src/ausweisapp/ +COPY docs/ /src/ausweisapp/docs/ +COPY CMakeLists.txt /src/ausweisapp/ +COPY cmake/ /src/ausweisapp/cmake/ +COPY resources/ /src/ausweisapp/resources/ +COPY src/ /src/ausweisapp/src/ +COPY test/ /src/ausweisapp/test/ + +RUN cmake /src/ausweisapp -B /build/app \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DBUILD_SHARED_LIBS=ON \ + -GNinja && \ + cmake --build /build/app && \ + ctest --test-dir /build/app --output-on-failure -j `nproc` && \ + cmake --install /build/app && \ + ccache -s -vv && rm -rf /build + +RUN find /usr/local/ -type d -empty -delete && \ + find /usr/local/lib/ -type f -not -name "*.so*" -delete && \ + find /usr/local/lib/ -type f -name "*.so*" -exec strip {} + && \ + strip /usr/local/bin/AusweisApp + + + +FROM alpine:$ALPINE_VERSION +ENV XDG_RUNTIME_DIR=/home/ausweisapp QT_QPA_PLATFORM=vnc + +COPY --from=builder /usr/local/lib /usr/local/lib +COPY --from=builder /usr/local/share /usr/local/share +COPY --from=builder /usr/local/bin/AusweisApp /usr/local/bin/AusweisApp + +RUN apk --no-cache upgrade -a && \ + apk --no-cache add tini pcsc-lite-libs eudev-libs doas ttf-freefont \ + http-parser qt6-qtbase qt6-qtsvg qt6-qtwebsockets qt6-qtdeclarative qt6-qtscxml qt6-qtconnectivity qt6-qtimageformats && \ + echo 'permit nopass :wheel' > /etc/doas.d/wheel.conf && \ + adduser ausweisapp -G wheel -s /bin/sh -D && \ + chmod 0700 /home/ausweisapp && mkdir -p /home/ausweisapp/.config && chown ausweisapp: /home/ausweisapp/.config + +USER ausweisapp +VOLUME ["/home/ausweisapp/.config"] +ENTRYPOINT ["/sbin/tini", "--"] +EXPOSE 5900/tcp 24727/tcp 24727/udp +CMD ["AusweisApp", "--address", "0.0.0.0", "--no-logfile"] diff -Nru ausweisapp2-2.3.1/Doxyfile.in ausweisapp2-2.4.0/Doxyfile.in --- ausweisapp2-2.3.1/Doxyfile.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/Doxyfile.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -# Available options -# http://www.stack.nl/~dimitri/doxygen/manual/config.html - -PROJECT_NAME = AusweisApp -OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/doc -OUTPUT_LANGUAGE = German -INPUT = @PROJECT_SOURCE_DIR@ -FILE_PATTERNS = *.cpp *.h -RECURSIVE = YES -OPTIMIZE_OUTPUT_C = YES -QT_AUTOBRIEF = YES -BUILTIN_STL_SUPPORT = YES -GENERATE_TREEVIEW = YES -FULL_PATH_NAMES = YES -STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ -EXCLUDE_PATTERNS = @PROJECT_BINARY_DIR@/* \ - */CMake* */libs/* */test/* - -SEARCHENGINE = YES -COLS_IN_ALPHA_INDEX = 10 - -EXTRACT_ALL = YES -GENERATE_TODOLIST = NO -CLASS_DIAGRAMS = YES -HIDE_UNDOC_RELATIONS = YES -HAVE_DOT = YES -DOT_NUM_THREADS = 0 -DOT_FONTNAME = Helvetica -DOT_FONTSIZE = 10 -CLASS_GRAPH = YES -COLLABORATION_GRAPH = NO -GROUP_GRAPHS = YES -UML_LOOK = YES -UML_LIMIT_NUM_FIELDS = 15 -TEMPLATE_RELATIONS = NO -INCLUDE_GRAPH = YES -INCLUDED_BY_GRAPH = YES -CALL_GRAPH = YES -CALLER_GRAPH = YES -GRAPHICAL_HIERARCHY = NO -DIRECTORY_GRAPH = NO -DOT_IMAGE_FORMAT = svg -INTERACTIVE_SVG = NO - -DOT_GRAPH_MAX_NODES = 50 -MAX_DOT_GRAPH_DEPTH = 0 -DOT_TRANSPARENT = NO -DOT_MULTI_TARGETS = NO -GENERATE_LEGEND = YES -DOT_CLEANUP = YES diff -Nru ausweisapp2-2.3.1/LICENSE.officially.txt ausweisapp2-2.4.0/LICENSE.officially.txt --- ausweisapp2-2.3.1/LICENSE.officially.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/LICENSE.officially.txt 2025-10-30 10:10:48.000000000 +0000 @@ -28,13 +28,9 @@ EUPL © Europäische Union 2007, 2016 -Diese Open-Source-Lizenz für die Europäische Union (»EUPL«) gilt für Werke (im Sinne der nachfolgenden Begriffsbestimmung), -die unter EUPL-Bedingungen zur Verfügung gestellt werden. Das Werk darf nur in der durch diese Lizenz -gestatteten Form genutzt werden (insoweit eine solche Nutzung dem Urheber vorbehalten ist). - -Das Werk wird unter den Bedingungen dieser Lizenz zur Verfügung gestellt, wenn der Lizenzgeber (im Sinne der -nachfolgenden Begriffsbestimmung) den folgenden Hinweis unmittelbar hinter dem Urheberrechtshinweis dieses Werks -anbringt: +Diese Open-Source-Lizenz für die Europäische Union (»EUPL«) gilt für Werke (im Sinne der nachfolgenden Begriffsbestimmung), die unter EUPL-Bedingungen zur Verfügung gestellt werden. Das Werk darf nur in der durch diese Lizenz gestatteten Form genutzt werden (insoweit eine solche Nutzung dem Urheber vorbehalten ist). + +Das Werk wird unter den Bedingungen dieser Lizenz zur Verfügung gestellt, wenn der Lizenzgeber (im Sinne der nachfolgenden Begriffsbestimmung) den folgenden Hinweis unmittelbar hinter dem Urheberrechtshinweis dieses Werks anbringt: Lizenziert unter der EUPL @@ -47,39 +43,27 @@ - »Lizenz«: diese Lizenz. -- »Originalwerk«: das Werk oder die Software, die vom Lizenzgeber unter dieser Lizenz verbreitet oder zugänglich - gemacht wird, und zwar als Quellcode und gegebenenfalls auch als ausführbarer Code. +- »Originalwerk«: das Werk oder die Software, die vom Lizenzgeber unter dieser Lizenz verbreitet oder zugänglich gemacht wird, und zwar als Quellcode und gegebenenfalls auch als ausführbarer Code. -- »Bearbeitungen«: die Werke oder Software, die der Lizenznehmer auf der Grundlage des Originalwerks oder seiner - Bearbeitungen schaffen kann. In dieser Lizenz wird nicht festgelegt, wie umfangreich die Änderung oder wie stark die - Abhängigkeit vom Originalwerk für eine Einstufung als Bearbeitung sein muss; dies bestimmt sich nach dem - Urheberrecht, das in dem unter Artikel 15 aufgeführten Land anwendbar ist. +- »Bearbeitungen«: die Werke oder Software, die der Lizenznehmer auf der Grundlage des Originalwerks oder seiner Bearbeitungen schaffen kann. In dieser Lizenz wird nicht festgelegt, wie umfangreich die Änderung oder wie stark die Abhängigkeit vom Originalwerk für eine Einstufung als Bearbeitung sein muss; dies bestimmt sich nach dem Urheberrecht, das in dem unter Artikel 15 aufgeführten Land anwendbar ist. - »Werk«: das Originalwerk oder seine Bearbeitungen. -- »Quellcode«: diejenige Form des Werkes, die zur Auffassung durch den Menschen bestimmt ist und die am besten - geeignet ist, um vom Menschen verstanden und verändert zu werden. +- »Quellcode«: diejenige Form des Werkes, die zur Auffassung durch den Menschen bestimmt ist und die am besten geeignet ist, um vom Menschen verstanden und verändert zu werden. -- »Ausführbarer Code«: die - üblicherweise - kompilierte Form des Werks, die von einem Computer als Programm - ausgeführt werden soll. +- »Ausführbarer Code«: die - üblicherweise - kompilierte Form des Werks, die von einem Computer als Programm ausgeführt werden soll. -- »Lizenzgeber«: die natürliche oder juristische Person, die das Werk unter der Lizenz verbreitet oder zugänglich - macht. +- »Lizenzgeber«: die natürliche oder juristische Person, die das Werk unter der Lizenz verbreitet oder zugänglich macht. -- »Bearbeiter«: jede natürliche oder juristische Person, die das Werk unter der Lizenz verändert oder auf andere Weise - zur Schaffung einer Bearbeitung beiträgt. +- »Bearbeiter«: jede natürliche oder juristische Person, die das Werk unter der Lizenz verändert oder auf andere Weise zur Schaffung einer Bearbeitung beiträgt. - »Lizenznehmer« (»Sie«): jede natürliche oder juristische Person, die das Werk unter den Lizenzbedingungen nutzt. -- »Verbreitung« oder »Zugänglichmachung«: alle Formen von Verkauf, Überlassung, Verleih, Vermietung, Verbreitung, - Weitergabe, Übermittlung oder anderweitiger Online- oder Offline-Bereitstellung von Vervielfältigungen des Werks - oder Zugänglichmachung seiner wesentlichen Funktionen für dritte natürliche oder juristische Personen. +- »Verbreitung« oder »Zugänglichmachung«: alle Formen von Verkauf, Überlassung, Verleih, Vermietung, Verbreitung, Weitergabe, Übermittlung oder anderweitiger Online- oder Offline-Bereitstellung von Vervielfältigungen des Werks oder Zugänglichmachung seiner wesentlichen Funktionen für dritte natürliche oder juristische Personen. 2. Umfang der Lizenzrechte - -Der Lizenzgeber erteilt Ihnen hiermit für die Gültigkeitsdauer der am Originalwerk bestehenden Urheberrechte eine -weltweite, unentgeltliche, nicht ausschließliche, unterlizenzierbare Lizenz, die Sie berechtigt: +Der Lizenzgeber erteilt Ihnen hiermit für die Gültigkeitsdauer der am Originalwerk bestehenden Urheberrechte eine Der Lizenzgeber erteilt Ihnen hiermit für die Gültigkeitsdauer der am Originalwerk bestehenden Urheberrechte eine weltweite, unentgeltliche, nicht ausschließliche, unterlizenzierbare Lizenz, die Sie berechtigt: - das Werk uneingeschränkt zu nutzen, @@ -87,8 +71,7 @@ - das Werk zu verändern und Bearbeitungen auf der Grundlage des Werks zu schaffen, -- das Werk öffentlich zugänglich zu machen, was das Recht einschließt, das Werk oder Vervielfältigungsstücke davon - öffentlich bereitzustellen oder wahrnehmbar zu machen oder das Werk, soweit möglich, öffentlich aufzuführen, +- das Werk öffentlich zugänglich zu machen, was das Recht einschließt, das Werk oder Vervielfältigungsstücke davon öffentlich bereitzustellen oder wahrnehmbar zu machen oder das Werk, soweit möglich, öffentlich aufzuführen, - das Werk oder Vervielfältigungen davon zu verbreiten, @@ -96,184 +79,114 @@ - das Werk oder Vervielfältigungen davon weiter zu lizenzieren. -Für die Wahrnehmung dieser Rechte können beliebige, derzeit bekannte oder künftige Medien, Träger und Formate -verwendet werden, soweit das geltende Recht dem nicht entgegensteht. +Für die Wahrnehmung dieser Rechte können beliebige, derzeit bekannte oder künftige Medien, Träger und Formate verwendet werden, soweit das geltende Recht dem nicht entgegensteht. -Für die Länder, in denen Urheberpersönlichkeitsrechte an dem Werk bestehen, verzichtet der Lizenzgeber im gesetzlich -zulässigen Umfang auf seine Urheberpersönlichkeitsrechte, um die Lizenzierung der oben aufgeführten -Verwertungsrechte wirksam durchführen zu können. +Für die Länder, in denen Urheberpersönlichkeitsrechte an dem Werk bestehen, verzichtet der Lizenzgeber im gesetzlich zulässigen Umfang auf seine Urheberpersönlichkeitsrechte, um die Lizenzierung der oben aufgeführten wirksam durchführen zu können. -Der Lizenzgeber erteilt dem Lizenznehmer ein nicht ausschließliches, unentgeltliches Nutzungsrecht an seinen Patenten, -sofern dies zur Ausübung der durch die Lizenz erteilten Nutzungsrechte am Werk notwendig ist. +Der Lizenzgeber erteilt dem Lizenznehmer ein nicht ausschließliches, unentgeltliches Nutzungsrecht an seinen Patenten, sofern dies zur Ausübung der durch die Lizenz erteilten Nutzungsrechte am Werk notwendig ist. 3. Zugänglichmachung des Quellcodes -Der Lizenzgeber kann das Werk entweder als Quellcode oder als ausführbaren Code zur Verfügung stellen. Stellt er es als -ausführbaren Code zur Verfügung, so stellt er darüber hinaus eine maschinenlesbare Kopie des Quellcodes für jedes von -ihm verbreitete Vervielfältigungsstück des Werks zur Verfügung, oder er verweist in einem Vermerk im Anschluss an den -dem Werk beigefügten Urheberrechtshinweis auf einen Speicherort, an dem problemlos und unentgeltlich auf den -Quellcode zugegriffen werden kann, solange der Lizenzgeber das Werk verbreitet oder zugänglich macht. +Der Lizenzgeber kann das Werk entweder als Quellcode oder als ausführbaren Code zur Verfügung stellen. Stellt er es als ausführbaren Code zur Verfügung, so stellt er darüber hinaus eine maschinenlesbare Kopie des Quellcodes für jedes von ihm verbreitete Vervielfältigungsstück des Werks zur Verfügung, oder er verweist in einem Vermerk im Anschluss an den dem Werk beigefügten Urheberrechtshinweis auf einen Speicherort, an dem problemlos und unentgeltlich auf den Quellcode zugegriffen werden kann, solange der Lizenzgeber das Werk verbreitet oder zugänglich macht. 4. Einschränkungen des Urheberrechts -Es ist nicht Zweck dieser Lizenz, Ausnahmen oder Schranken der ausschließlichen Rechte des Urhebers am Werk, die -dem Lizenznehmer zugutekommen, einzuschränken. Auch die Erschöpfung dieser Rechte bleibt von dieser Lizenz -unberührt. +Es ist nicht Zweck dieser Lizenz, Ausnahmen oder Schranken der ausschließlichen Rechte des Urhebers am Werk, die dem Lizenznehmer zugutekommen, einzuschränken. Auch die Erschöpfung dieser Rechte bleibt von dieser Lizenz . 5. Pflichten des Lizenznehmers -Die Einräumung der oben genannten Rechte ist an mehrere Beschränkungen und Pflichten für den Lizenznehmer -gebunden: +Die Einräumung der oben genannten Rechte ist an mehrere Beschränkungen und Pflichten für den Lizenznehmer gebunden: + +Urheberrechtshinweis, Lizenztext, Nennung des Bearbeiters: Der Lizenznehmer muss alle Urheberrechts-, Patent- oder Markenrechtshinweise und alle Hinweise auf die Lizenz und den Haftungsausschluss unverändert lassen. Jedem von ihm verbreiteten oder zugänglich gemachten Vervielfältigungsstück des Werks muss der Lizenznehmer diese Hinweise sowie diese Lizenz beifügen. Der Lizenznehmer muss auf jedem abgeleiteten Werk deutlich darauf hinweisen, dass das geändert wurde, und das Datum der Bearbeitung angeben. + +»Copyleft«-Klausel: Der Lizenznehmer darf Vervielfältigungen des Originalwerks oder Bearbeitungen nur unter den Bedingungen dieser EUPL oder einer neueren Version dieser Lizenz verbreiten oder zugänglich machen, außer wenn das Originalwerk ausdrücklich nur unter dieser Lizenzversion - z. B. mit der Angabe »Nur EUPL V. 1.2« - verbreitet werden darf. Der Lizenznehmer (der zum Lizenzgeber wird) darf für das Werk oder die Bearbeitung keine zusätzlichen Bedingungen anbieten oder vorschreiben, die die Bedingungen dieser Lizenz verändern oder einschränken. -Urheberrechtshinweis, Lizenztext, Nennung des Bearbeiters: Der Lizenznehmer muss alle Urheberrechts-, Patent- -oder Markenrechtshinweise und alle Hinweise auf die Lizenz und den Haftungsausschluss unverändert lassen. Jedem von -ihm verbreiteten oder zugänglich gemachten Vervielfältigungsstück des Werks muss der Lizenznehmer diese Hinweise -sowie diese Lizenz beifügen. Der Lizenznehmer muss auf jedem abgeleiteten Werk deutlich darauf hinweisen, dass das -Werk geändert wurde, und das Datum der Bearbeitung angeben. - -»Copyleft«-Klausel: Der Lizenznehmer darf Vervielfältigungen des Originalwerks oder Bearbeitungen nur unter den -Bedingungen dieser EUPL oder einer neueren Version dieser Lizenz verbreiten oder zugänglich machen, außer wenn das -Originalwerk ausdrücklich nur unter dieser Lizenzversion - z. B. mit der Angabe »Nur EUPL V. 1.2« - verbreitet -werden darf. Der Lizenznehmer (der zum Lizenzgeber wird) darf für das Werk oder die Bearbeitung keine zusätzlichen -Bedingungen anbieten oder vorschreiben, die die Bedingungen dieser Lizenz verändern oder einschränken. - -Kompatibilitäts-Klausel: Wenn der Lizenznehmer Bearbeitungen, die auf dem Werk und einem anderen Werk, das -unter einer kompatiblen Lizenz lizenziert wurde, basieren, oder die Kopien dieser Bearbeitungen verbreitet oder -zugänglich macht, kann dies unter den Bedingungen dieser kompatiblen Lizenz erfolgen. Unter »kompatibler Lizenz« ist -eine im Anhang dieser Lizenz angeführte Lizenz zu verstehen. Sollten die Verpflichtungen des Lizenznehmers aus der -kompatiblen Lizenz mit denjenigen aus der vorliegenden Lizenz (EUPL) in Konflikt stehen, werden die Verpflichtungen -aus der kompatiblen Lizenz Vorrang haben. - -Bereitstellung des Quellcodes: Wenn der Lizenznehmer Vervielfältigungsstücke des Werks verbreitet oder zugänglich -macht, muss er eine maschinenlesbare Fassung des Quellcodes mitliefern oder einen Speicherort angeben, über den -problemlos und unentgeltlich so lange auf diesen Quellcode zugegriffen werden kann, wie der Lizenznehmer das Werk -verbreitet oder zugänglich macht. - -Rechtsschutz: Diese Lizenz erlaubt nicht die Benutzung von Kennzeichen, Marken oder geschützten Namensrechten des -Lizenzgebers, soweit dies nicht für die angemessene und übliche Beschreibung der Herkunft des Werks und der -inhaltlichen Wiedergabe des Urheberrechtshinweises erforderlich ist. +Kompatibilitäts-Klausel: Wenn der Lizenznehmer Bearbeitungen, die auf dem Werk und einem anderen Werk, das unter einer kompatiblen Lizenz lizenziert wurde, basieren, oder die Kopien dieser Bearbeitungen verbreitet oder zugänglich macht, kann dies unter den Bedingungen dieser kompatiblen Lizenz erfolgen. Unter »kompatibler Lizenz« ist eine im Anhang dieser Lizenz angeführte Lizenz zu verstehen. Sollten die Verpflichtungen des Lizenznehmers aus der kompatiblen Lizenz mit denjenigen aus der vorliegenden Lizenz (EUPL) in Konflikt stehen, werden die Verpflichtungen aus der kompatiblen Lizenz Vorrang haben. + +Bereitstellung des Quellcodes: Wenn der Lizenznehmer Vervielfältigungsstücke des Werks verbreitet oder zugänglich macht, muss er eine maschinenlesbare Fassung des Quellcodes mitliefern oder einen Speicherort angeben, über den problemlos und unentgeltlich so lange auf diesen Quellcode zugegriffen werden kann, wie der Lizenznehmer das Werk verbreitet oder zugänglich macht. + +Rechtsschutz: Diese Lizenz erlaubt nicht die Benutzung von Kennzeichen, Marken oder geschützten Namensrechten des Lizenzgebers, soweit dies nicht für die angemessene und übliche Beschreibung der Herkunft des Werks und der inhaltlichen Wiedergabe des Urheberrechtshinweises erforderlich ist. 6. Urheber und Bearbeiter -Der ursprüngliche Lizenzgeber gewährleistet, dass er das Urheberrecht am Originalwerk innehat oder dieses an ihn -lizenziert wurde und dass er befugt ist, diese Lizenz zu erteilen. +Der ursprüngliche Lizenzgeber gewährleistet, dass er das Urheberrecht am Originalwerk innehat oder dieses an ihn lizenziert wurde und dass er befugt ist, diese Lizenz zu erteilen. -Jeder Bearbeiter gewährleistet, dass er das Urheberrecht an den von ihm vorgenommenen Änderungen des Werks besitzt -und befugt ist, diese Lizenz zu erteilen. +Jeder Bearbeiter gewährleistet, dass er das Urheberrecht an den von ihm vorgenommenen Änderungen des Werks besitzt und befugt ist, diese Lizenz zu erteilen. -Jedes Mal, wenn Sie die Lizenz annehmen, erteilen Ihnen der ursprüngliche Lizenzgeber und alle folgenden Bearbeiter -eine Befugnis zur Nutzung ihrer Beiträge zum Werk unter den Bedingungen dieser Lizenz. +Jedes Mal, wenn Sie die Lizenz annehmen, erteilen Ihnen der ursprüngliche Lizenzgeber und alle folgenden Bearbeiter eine Befugnis zur Nutzung ihrer Beiträge zum Werk unter den Bedingungen dieser Lizenz. 7. Gewährleistungsausschluss -Die Arbeit an diesem Werk wird laufend fortgeführt; es wird durch unzählige Bearbeiter ständig verbessert. Das Werk ist -nicht vollendet und kann daher Fehler (»bugs«) enthalten, die dieser Art der Entwicklung inhärent sind. +Die Arbeit an diesem Werk wird laufend fortgeführt; es wird durch unzählige Bearbeiter ständig verbessert. Das Werk ist nicht vollendet und kann daher Fehler (»bugs«) enthalten, die dieser Art der Entwicklung inhärent sind. -Aus den genannten Gründen wird das Werk unter dieser Lizenz »so, wie es ist« ohne jegliche Gewährleistung zur -Verfügung gestellt. Dies gilt unter anderem - aber nicht ausschließlich - für Marktreife, Verwendbarkeit für einen -bestimmten Zweck, Mängelfreiheit, Richtigkeit sowie Nichtverletzung von anderen Immaterialgüterrechten als dem -Urheberrecht (vgl. dazu Artikel 6 dieser Lizenz). +Aus den genannten Gründen wird das Werk unter dieser Lizenz »so, wie es ist« ohne jegliche Gewährleistung zur Verfügung gestellt. Dies gilt unter anderem - aber nicht ausschließlich - für Marktreife, Verwendbarkeit für einen bestimmten Zweck, Mängelfreiheit, Richtigkeit sowie Nichtverletzung von anderen Immaterialgüterrechten als dem Urheberrecht (vgl. dazu Artikel 6 dieser Lizenz). -Dieser Gewährleistungsausschluss ist wesentlicher Bestandteil der Lizenz und Bedingung für die Einräumung von -Rechten an dem Werk. +Dieser Gewährleistungsausschluss ist wesentlicher Bestandteil der Lizenz und Bedingung für die Einräumung von Rechten an dem Werk. 8. Haftungsausschluss/Haftungsbeschränkung -Außer in Fällen von Vorsatz oder der Verursachung von Personenschäden haftet der Lizenzgeber nicht für direkte oder -indirekte, materielle oder immaterielle Schäden irgendwelcher Art, die aus der Lizenz oder der Benutzung des Werks -folgen; dies gilt unter anderem, aber nicht ausschließlich, für Firmenwertverluste, Produktionsausfall, Computerausfall -oder Computerfehler, Datenverlust oder wirtschaftliche Schäden, und zwar auch dann, wenn der Lizenzgeber auf die -Möglichkeit solcher Schäden hingewiesen wurde. Unabhängig davon haftet der Lizenzgeber im Rahmen der gesetzlichen -Produkthaftung, soweit die entsprechenden Regelungen auf das Werk anwendbar sind. +Außer in Fällen von Vorsatz oder der Verursachung von Personenschäden haftet der Lizenzgeber nicht für direkte oder indirekte, materielle oder immaterielle Schäden irgendwelcher Art, die aus der Lizenz oder der Benutzung des Werks folgen; dies gilt unter anderem, aber nicht ausschließlich, für Firmenwertverluste, Produktionsausfall, Computerausfall oder Computerfehler, Datenverlust oder wirtschaftliche Schäden, und zwar auch dann, wenn der Lizenzgeber auf die Möglichkeit solcher Schäden hingewiesen wurde. Unabhängig davon haftet der Lizenzgeber im Rahmen der gesetzlichen Produkthaftung, soweit die entsprechenden Regelungen auf das Werk anwendbar sind. 9. Zusatzvereinbarungen -Wenn Sie das Werk verbreiten, können Sie Zusatzvereinbarungen schließen, in denen Verpflichtungen oder -Dienstleistungen festgelegt werden, die mit dieser Lizenz vereinbar sind. Sie dürfen Verpflichtungen indessen nur in -Ihrem eigenen Namen und auf Ihre eigene Verantwortung eingehen, nicht jedoch im Namen des ursprünglichen -Lizenzgebers oder eines anderen Bearbeiters, und nur, wenn Sie sich gegenüber allen Bearbeitern verpflichten, sie zu -entschädigen, zu verteidigen und von der Haftung freizustellen, falls aufgrund der von Ihnen eingegangenen -Gewährleistungsverpflichtung oder Haftungsübernahme Forderungen gegen sie geltend gemacht werden oder eine -Haftungsverpflichtung entsteht. +Wenn Sie das Werk verbreiten, können Sie Zusatzvereinbarungen schließen, in denen Verpflichtungen oder Dienstleistungen festgelegt werden, die mit dieser Lizenz vereinbar sind. Sie dürfen Verpflichtungen indessen nur in Ihrem eigenen Namen und auf Ihre eigene Verantwortung eingehen, nicht jedoch im Namen des ursprünglichen Lizenzgebers oder eines anderen Bearbeiters, und nur, wenn Sie sich gegenüber allen Bearbeitern verpflichten, sie zu entschädigen, zu verteidigen und von der Haftung freizustellen, falls aufgrund der von Ihnen eingegangenen Gewährleistungsverpflichtung oder Haftungsübernahme Forderungen gegen sie geltend gemacht werden oder eine Haftungsverpflichtung entsteht. 10. Annahme der Lizenz -Sie können den Bestimmungen dieser Lizenz zustimmen, indem Sie das Symbol »Lizenz annehmen« unter dem Fenster -mit dem Lizenztext anklicken oder indem Sie Ihre Zustimmung auf vergleichbare Weise in einer nach anwendbarem -Recht zulässigen Form geben. Das Anklicken des Symbols gilt als Anzeichen Ihrer eindeutigen und unwiderruflichen -Annahme der Lizenz und der darin enthaltenen Klauseln und Bedingungen. - -In gleicher Weise gilt als Zeichen der eindeutigen und unwiderruflichen Zustimmung die Ausübung eines Rechtes, das in -Artikel 2 dieser Lizenz angeführt ist, wie das Erstellen einer Bearbeitung oder die Verbreitung oder Zugänglichmachung -des Werks oder dessen Vervielfältigungen. +Sie können den Bestimmungen dieser Lizenz zustimmen, indem Sie das Symbol »Lizenz annehmen« unter dem Fenster mit dem Lizenztext anklicken oder indem Sie Ihre Zustimmung auf vergleichbare Weise in einer nach anwendbarem Recht zulässigen Form geben. Das Anklicken des Symbols gilt als Anzeichen Ihrer eindeutigen und unwiderruflichen Annahme der Lizenz und der darin enthaltenen Klauseln und Bedingungen. + +In gleicher Weise gilt als Zeichen der eindeutigen und unwiderruflichen Zustimmung die Ausübung eines Rechtes, das in Artikel 2 dieser Lizenz angeführt ist, wie das Erstellen einer Bearbeitung oder die Verbreitung oder Zugänglichmachung des Werks oder dessen Vervielfältigungen. 11. Informationspflichten -Wenn Sie das Werk verbreiten oder zugänglich machen (beispielsweise, indem Sie es zum Herunterladen von einer -Website anbieten), müssen Sie über den Vertriebskanal oder das benutzte Verbreitungsmedium der Öffentlichkeit -zumindest jene Informationen bereitstellen, die nach dem anwendbaren Recht bezüglich der Lizenzgeber, der Lizenz und -ihrer Zugänglichkeit, des Abschlusses des Lizenzvertrags sowie darüber, wie die Lizenz durch den Lizenznehmer -gespeichert und vervielfältigt werden kann, erforderlich sind. +Wenn Sie das Werk verbreiten oder zugänglich machen (beispielsweise, indem Sie es zum Herunterladen von einer Website anbieten), müssen Sie über den Vertriebskanal oder das benutzte Verbreitungsmedium der Öffentlichkeit zumindest jene Informationen bereitstellen, die nach dem anwendbaren Recht bezüglich der Lizenzgeber, der Lizenz und ihrer Zugänglichkeit, des Abschlusses des Lizenzvertrags sowie darüber, wie die Lizenz durch den Lizenznehmer gespeichert und vervielfältigt werden kann, erforderlich sind. 12. Beendigung der Lizenz -Die Lizenz und die damit eingeräumten Rechte erlöschen automatisch, wenn der Lizenznehmer gegen die Lizenzbedingungen -verstößt. +Die Lizenz und die damit eingeräumten Rechte erlöschen automatisch, wenn der Lizenznehmer gegen die Lizenzbedingungen verstößt. -Ein solches Erlöschen der Lizenz führt nicht zum Erlöschen der Lizenzen von Personen, denen das Werk vom -Lizenznehmer unter dieser Lizenz zur Verfügung gestellt worden ist, solange diese Personen die Lizenzbedingungen -erfüllen. +Ein solches Erlöschen der Lizenz führt nicht zum Erlöschen der Lizenzen von Personen, denen das Werk vom Lizenznehmer unter dieser Lizenz zur Verfügung gestellt worden ist, solange diese Personen die Lizenzbedingungen erfüllen. 13. Sonstiges Unbeschadet des Artikels 9 stellt die Lizenz die vollständige Vereinbarung der Parteien über das Werk dar. -Sind einzelne Bestimmungen der Lizenz nach geltendem Recht nichtig oder unwirksam, so berührt dies nicht die -Wirksamkeit oder Durchsetzbarkeit der Lizenz an sich. Solche Bestimmungen werden vielmehr so ausgelegt oder -modifiziert, dass sie wirksam und durchsetzbar sind. - -Die Europäische Kommission kann weitere Sprachfassungen oder neue Versionen dieser Lizenz oder aktualisierte -Fassungen des Anhangs veröffentlichen, soweit dies notwendig und angemessen ist, ohne den Umfang der Lizenzrechte -zu verringern. Neue Versionen werden mit einer eindeutigen Versionsnummer veröffentlicht. +Sind einzelne Bestimmungen der Lizenz nach geltendem Recht nichtig oder unwirksam, so berührt dies nicht die Wirksamkeit oder Durchsetzbarkeit der Lizenz an sich. Solche Bestimmungen werden vielmehr so ausgelegt oder modifiziert, dass sie wirksam und durchsetzbar sind. -Alle von der Europäischen Kommission anerkannten Sprachfassungen dieser Lizenz sind gleichwertig. Die Parteien -können sich auf die Sprachfassung ihrer Wahl berufen. +Die Europäische Kommission kann weitere Sprachfassungen oder neue Versionen dieser Lizenz oder aktualisierte Fassungen des Anhangs veröffentlichen, soweit dies notwendig und angemessen ist, ohne den Umfang der Lizenzrechte zu verringern. Neue Versionen werden mit einer eindeutigen Versionsnummer veröffentlicht. + +Alle von der Europäischen Kommission anerkannten Sprachfassungen dieser Lizenz sind gleichwertig. Die Parteien können sich auf die Sprachfassung ihrer Wahl berufen. 14. Gerichtsstand Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: -- Für alle Streitigkeiten über die Auslegung dieser Lizenz zwischen den Organen, Einrichtungen und sonstigen Stellen - der Europäischen Union als Lizenzgeber und einem Lizenznehmer ist der Gerichtshof der Europäischen Union - gemäß Artikel 272 des Vertrags über die Arbeitsweise der Europäischen Union zuständig; +- Für alle Streitigkeiten über die Auslegung dieser Lizenz zwischen den Organen, Einrichtungen und sonstigen Stellen der Europäischen Union als Lizenzgeber und einem Lizenznehmer ist der Gerichtshof der Europäischen Union gemäß Artikel 272 des Vertrags über die Arbeitsweise der Europäischen Union zuständig; -- Gerichtsstand für Streitigkeiten zwischen anderen Parteien über die Auslegung dieser Lizenz ist allein der Ort, an - dem der Lizenzgeber seinen Wohnsitz oder den wirtschaftlichen Mittelpunkt seiner Tätigkeit hat. +- Gerichtsstand für Streitigkeiten zwischen anderen Parteien über die Auslegung dieser Lizenz ist allein der Ort, an dem der Lizenzgeber seinen Wohnsitz oder den wirtschaftlichen Mittelpunkt seiner Tätigkeit hat. 15. Anwendbares Recht Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: -- Diese Lizenz unterliegt dem Recht des Mitgliedstaats der Europäischen Union, in dem der Lizenzgeber seinen Sitz, - Wohnsitz oder eingetragenen Sitz hat; +- Diese Lizenz unterliegt dem Recht des Mitgliedstaats der Europäischen Union, in dem der Lizenzgeber seinen Sitz, Wohnsitz oder eingetragenen Sitz hat; -- diese Lizenz unterliegt dem belgischen Recht, wenn der Lizenzgeber keinen Sitz, Wohnsitz oder eingetragenen Sitz in - einem Mitgliedstaat der Europäischen Union hat. +- diese Lizenz unterliegt dem belgischen Recht, wenn der Lizenzgeber keinen Sitz, Wohnsitz oder eingetragenen Sitz in einem Mitgliedstaat der Europäischen Union hat. Anlage @@ -300,12 +213,9 @@ - Québec Free and Open-Source Licence - Reciprocity (LiLiQ-R) oder Strong Reciprocity (LiLiQ-R+) -- Die Europäische Kommission kann diesen Anhang aktualisieren, um neuere Fassungen der obigen Lizenzen - aufzunehmen, ohne hierfür eine neue Fassung der EUPL auszuarbeiten, solange diese Lizenzen die in Artikel 2 - gewährten Rechte gewährleisten und den erfassten Quellcode vor ausschließlicher Aneignung schützen. +- Die Europäische Kommission kann diesen Anhang aktualisieren, um neuere Fassungen der obigen Lizenzen aufzunehmen, ohne hierfür eine neue Fassung der EUPL auszuarbeiten, solange diese Lizenzen die in Artikel 2 gewährten Rechte gewährleisten und den erfassten Quellcode vor ausschließlicher Aneignung schützen. -- Alle sonstigen Änderungen oder Ergänzungen dieses Anhangs bedürfen der Ausarbeitung einer neuen Version der - EUPL. +- Alle sonstigen Änderungen oder Ergänzungen dieses Anhangs bedürfen der Ausarbeitung einer neuen Version der EUPL. @@ -314,7 +224,7 @@ B. ZUSATZVEREINBARUNGEN § 1 Nutzungsbedingungen -(1) Diese Allgemeine Geschäftsbedingungen (AGB) des Bundes, vertreten durch das Bundesministerium des Innern und für Heimat, vertreten durch das Bundesamt für Sicherheit in der Informationstechnik (nachfolgend "Bund") und dem Nutzer gelten für die Überlassung und Nutzung der Software AusweisApp (nachfolgend AusweisApp) und deren neue Versionen, die auf der Grundlage dieser Bedingungen überlassen werden. +(1) Diese Allgemeine Geschäftsbedingungen (AGB) des Bundes, vertreten durch das Bundesministerium des Innern, vertreten durch das Bundesamt für Sicherheit in der Informationstechnik (nachfolgend "Bund") und dem Nutzer gelten für die Überlassung und Nutzung der Software AusweisApp (nachfolgend AusweisApp) und deren neue Versionen, die auf der Grundlage dieser Bedingungen überlassen werden. (2) "Nutzer" im Sinne dieses Vertrages sind natürliche Personen. (3) Diese Zusatzvereinbarungen lassen die Rechte und Pflichten aus der EUPL unberührt. @@ -350,12 +260,12 @@ OpenSSL Lizenz: Apache 2.0 - Version: 3.4.1 + Version: 3.5.4 Adresse: https://www.openssl.org/ Qt Lizenz: LGPL v3 - Version: 6.8.1 + Version: 6.9.2 Adresse: https://www.qt.io/ http_parser @@ -363,11 +273,6 @@ Version: 2.9.4 Adresse: https://github.com/nodejs/http-parser/ -AndroidX Core Library - Lizenz: Apache 2.0 - Version: 1.9.0 - Adresse: https://developer.android.com/jetpack/androidx - Die oben genannten Lizenztexte lauten in ihrer originalen Fassung wie folgt: @@ -379,10 +284,7 @@ The Qt Toolkit is Copyright (C) 2016 The Qt Company Ltd. Contact: http://www.qt.io/licensing/ -You may use, distribute and copy the Qt Toolkit under the terms of -GNU Lesser General Public License version 3, which is displayed below. -This license makes reference to the version 3 of the GNU General -Public License, which you can find in the LICENSE.GPLv3 file. +You may use, distribute and copy the Qt Toolkit under the terms of GNU Lesser General Public License version 3, which is displayed below. This license makes reference to the version 3 of the GNU General Public License, which you can find in the LICENSE.GPLv3 file. ------------------------------------------------------------------------- @@ -390,164 +292,77 @@ Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. -Everyone is permitted to copy and distribute verbatim copies of this -licensedocument, but changing it is not allowed. +Everyone is permitted to copy and distribute verbatim copies of this licensedocument, but changing it is not allowed. -This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. +This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. -As used herein, «this License» refers to version 3 of the GNU Lesser -General Public License, and the «GNU GPL» refers to version 3 of the -GNU General Public License. - -«The Library» refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - -An «Application» is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - -A «Combined Work» is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the «Linked -Version». - -The «Minimal Corresponding Source» for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - -The «Corresponding Application Code» for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. +As used herein, «this License» refers to version 3 of the GNU Lesser General Public License, and the «GNU GPL» refers to version 3 of the GNU General Public License. + +«The Library» refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. + +An «Application» is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. + +A «Combined Work» is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the «Linked Version». + +The «Minimal Corresponding Source» for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. + +The «Corresponding Application Code» for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. -You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. +You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. -If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort - to ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or +If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: + + a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. + b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. -The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that - the Library is used in it and that the Library and its use are - covered by this License. +The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: - b) Accompany the object code with a copy of the GNU GPL and this - license document. + a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. -You may convey a Combined Work under terms of your choice that, taken -together, effectively do not restrict modification of the portions of -the Library contained in the Combined Work and reverse engineering for -debugging such modifications, if you also do each of the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this - license document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. +You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license document. + + c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: - 0) Convey the Minimal Corresponding Source under the terms of - this License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with - the Library. A suitable mechanism is one that (a) uses at run - time a copy of the Library already present on the user's - computer system, and (b) will operate properly with a modified - version of the Library that is interface-compatible with the - Linked Version. - - e) Provide Installation Information, but only if you would - otherwise be required to provide such information under section 6 - of the GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the Application - with a modified version of the Linked Version. (If you use option - 4d0, the Installation Information must accompany the Minimal - Corresponding Source and Corresponding Application Code. If you - use option 4d1, you must provide the Installation Information in - the manner specified by section 6 of the GNU GPL for conveying - Corresponding Source.) + 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. + + e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. -You may place library facilities that are a work based on the Library -side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities, conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of - it is a work based on the Library, and explaining where to find - the accompanying uncombined form of the same work. +You may place library facilities that are a work based on the Library by side in a single library together with other library that are not Applications and are not covered by this , and convey such a combined library under terms of your , if you do both of the following: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. -The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -as you received it specifies that a certain numbered version of the -GNU Lesser General Public License «or any later version» applies to -it, you have the option of following the terms and conditions either -of that published version or of any later version published by the -Free Software Foundation. If the Library as you received it does not -specify a version number of the GNU Lesser General Public License, -you may choose any version of the GNU Lesser General Public License -ever published by the Free Software Foundation. - -If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the Library. +The Free Software Foundation may publish revised and/or new versions the GNU Lesser General Public License from time to time. Such new will be similar in spirit to the present version, but may in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library you received it specifies that a certain numbered version of the Lesser General Public License «or any later version» applies to , you have the option of following the terms and conditions either that published version or of any later version published by the Software Foundation. If the Library as you received it does not a version number of the GNU Lesser General Public License, may choose any version of the GNU Lesser General Public License published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide future versions of the GNU Lesser General Public License shall , that proxy's public statement of acceptance of any version is authorization for you to choose that version for the Library. @@ -558,23 +373,11 @@ Copyright Joyent, Inc. and other Node contributors. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. +Permission is hereby granted, free of charge, to any person obtaining a copy this software and associated documentation files (the "Software"), to in the Software without restriction, including without limitation the to use, copy, modify, merge, publish, distribute, sublicense, and/or copies of the Software, and to permit persons to whom the Software is to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER , WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS THE SOFTWARE. @@ -589,172 +392,50 @@ 1. Definitions. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - END OF TERMS AND CONDITIONS + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff -Nru ausweisapp2-2.3.1/LICENSE.txt ausweisapp2-2.4.0/LICENSE.txt --- ausweisapp2-2.3.1/LICENSE.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/LICENSE.txt 2025-10-30 10:10:48.000000000 +0000 @@ -27,13 +27,9 @@ EUPL © Europäische Union 2007, 2016 -Diese Open-Source-Lizenz für die Europäische Union (»EUPL«) gilt für Werke (im Sinne der nachfolgenden Begriffsbestimmung), -die unter EUPL-Bedingungen zur Verfügung gestellt werden. Das Werk darf nur in der durch diese Lizenz -gestatteten Form genutzt werden (insoweit eine solche Nutzung dem Urheber vorbehalten ist). - -Das Werk wird unter den Bedingungen dieser Lizenz zur Verfügung gestellt, wenn der Lizenzgeber (im Sinne der -nachfolgenden Begriffsbestimmung) den folgenden Hinweis unmittelbar hinter dem Urheberrechtshinweis dieses Werks -anbringt: +Diese Open-Source-Lizenz für die Europäische Union (»EUPL«) gilt für Werke (im Sinne der nachfolgenden Begriffsbestimmung), die unter EUPL-Bedingungen zur Verfügung gestellt werden. Das Werk darf nur in der durch diese Lizenz gestatteten Form genutzt werden (insoweit eine solche Nutzung dem Urheber vorbehalten ist). + +Das Werk wird unter den Bedingungen dieser Lizenz zur Verfügung gestellt, wenn der Lizenzgeber (im Sinne der nachfolgenden Begriffsbestimmung) den folgenden Hinweis unmittelbar hinter dem Urheberrechtshinweis dieses Werks anbringt: Lizenziert unter der EUPL @@ -46,39 +42,27 @@ - »Lizenz«: diese Lizenz. -- »Originalwerk«: das Werk oder die Software, die vom Lizenzgeber unter dieser Lizenz verbreitet oder zugänglich - gemacht wird, und zwar als Quellcode und gegebenenfalls auch als ausführbarer Code. +- »Originalwerk«: das Werk oder die Software, die vom Lizenzgeber unter dieser Lizenz verbreitet oder zugänglich gemacht wird, und zwar als Quellcode und gegebenenfalls auch als ausführbarer Code. -- »Bearbeitungen«: die Werke oder Software, die der Lizenznehmer auf der Grundlage des Originalwerks oder seiner - Bearbeitungen schaffen kann. In dieser Lizenz wird nicht festgelegt, wie umfangreich die Änderung oder wie stark die - Abhängigkeit vom Originalwerk für eine Einstufung als Bearbeitung sein muss; dies bestimmt sich nach dem - Urheberrecht, das in dem unter Artikel 15 aufgeführten Land anwendbar ist. +- »Bearbeitungen«: die Werke oder Software, die der Lizenznehmer auf der Grundlage des Originalwerks oder seiner Bearbeitungen schaffen kann. In dieser Lizenz wird nicht festgelegt, wie umfangreich die Änderung oder wie stark die Abhängigkeit vom Originalwerk für eine Einstufung als Bearbeitung sein muss; dies bestimmt sich nach dem Urheberrecht, das in dem unter Artikel 15 aufgeführten Land anwendbar ist. - »Werk«: das Originalwerk oder seine Bearbeitungen. -- »Quellcode«: diejenige Form des Werkes, die zur Auffassung durch den Menschen bestimmt ist und die am besten - geeignet ist, um vom Menschen verstanden und verändert zu werden. +- »Quellcode«: diejenige Form des Werkes, die zur Auffassung durch den Menschen bestimmt ist und die am besten geeignet ist, um vom Menschen verstanden und verändert zu werden. -- »Ausführbarer Code«: die - üblicherweise - kompilierte Form des Werks, die von einem Computer als Programm - ausgeführt werden soll. +- »Ausführbarer Code«: die - üblicherweise - kompilierte Form des Werks, die von einem Computer als Programm ausgeführt werden soll. -- »Lizenzgeber«: die natürliche oder juristische Person, die das Werk unter der Lizenz verbreitet oder zugänglich - macht. +- »Lizenzgeber«: die natürliche oder juristische Person, die das Werk unter der Lizenz verbreitet oder zugänglich macht. -- »Bearbeiter«: jede natürliche oder juristische Person, die das Werk unter der Lizenz verändert oder auf andere Weise - zur Schaffung einer Bearbeitung beiträgt. +- »Bearbeiter«: jede natürliche oder juristische Person, die das Werk unter der Lizenz verändert oder auf andere Weise zur Schaffung einer Bearbeitung beiträgt. - »Lizenznehmer« (»Sie«): jede natürliche oder juristische Person, die das Werk unter den Lizenzbedingungen nutzt. -- »Verbreitung« oder »Zugänglichmachung«: alle Formen von Verkauf, Überlassung, Verleih, Vermietung, Verbreitung, - Weitergabe, Übermittlung oder anderweitiger Online- oder Offline-Bereitstellung von Vervielfältigungen des Werks - oder Zugänglichmachung seiner wesentlichen Funktionen für dritte natürliche oder juristische Personen. +- »Verbreitung« oder »Zugänglichmachung«: alle Formen von Verkauf, Überlassung, Verleih, Vermietung, Verbreitung, Weitergabe, Übermittlung oder anderweitiger Online- oder Offline-Bereitstellung von Vervielfältigungen des Werks oder Zugänglichmachung seiner wesentlichen Funktionen für dritte natürliche oder juristische Personen. 2. Umfang der Lizenzrechte - -Der Lizenzgeber erteilt Ihnen hiermit für die Gültigkeitsdauer der am Originalwerk bestehenden Urheberrechte eine -weltweite, unentgeltliche, nicht ausschließliche, unterlizenzierbare Lizenz, die Sie berechtigt: +Der Lizenzgeber erteilt Ihnen hiermit für die Gültigkeitsdauer der am Originalwerk bestehenden Urheberrechte eine Der Lizenzgeber erteilt Ihnen hiermit für die Gültigkeitsdauer der am Originalwerk bestehenden Urheberrechte eine weltweite, unentgeltliche, nicht ausschließliche, unterlizenzierbare Lizenz, die Sie berechtigt: - das Werk uneingeschränkt zu nutzen, @@ -86,8 +70,7 @@ - das Werk zu verändern und Bearbeitungen auf der Grundlage des Werks zu schaffen, -- das Werk öffentlich zugänglich zu machen, was das Recht einschließt, das Werk oder Vervielfältigungsstücke davon - öffentlich bereitzustellen oder wahrnehmbar zu machen oder das Werk, soweit möglich, öffentlich aufzuführen, +- das Werk öffentlich zugänglich zu machen, was das Recht einschließt, das Werk oder Vervielfältigungsstücke davon öffentlich bereitzustellen oder wahrnehmbar zu machen oder das Werk, soweit möglich, öffentlich aufzuführen, - das Werk oder Vervielfältigungen davon zu verbreiten, @@ -95,184 +78,114 @@ - das Werk oder Vervielfältigungen davon weiter zu lizenzieren. -Für die Wahrnehmung dieser Rechte können beliebige, derzeit bekannte oder künftige Medien, Träger und Formate -verwendet werden, soweit das geltende Recht dem nicht entgegensteht. +Für die Wahrnehmung dieser Rechte können beliebige, derzeit bekannte oder künftige Medien, Träger und Formate verwendet werden, soweit das geltende Recht dem nicht entgegensteht. -Für die Länder, in denen Urheberpersönlichkeitsrechte an dem Werk bestehen, verzichtet der Lizenzgeber im gesetzlich -zulässigen Umfang auf seine Urheberpersönlichkeitsrechte, um die Lizenzierung der oben aufgeführten -Verwertungsrechte wirksam durchführen zu können. +Für die Länder, in denen Urheberpersönlichkeitsrechte an dem Werk bestehen, verzichtet der Lizenzgeber im gesetzlich zulässigen Umfang auf seine Urheberpersönlichkeitsrechte, um die Lizenzierung der oben aufgeführten wirksam durchführen zu können. -Der Lizenzgeber erteilt dem Lizenznehmer ein nicht ausschließliches, unentgeltliches Nutzungsrecht an seinen Patenten, -sofern dies zur Ausübung der durch die Lizenz erteilten Nutzungsrechte am Werk notwendig ist. +Der Lizenzgeber erteilt dem Lizenznehmer ein nicht ausschließliches, unentgeltliches Nutzungsrecht an seinen Patenten, sofern dies zur Ausübung der durch die Lizenz erteilten Nutzungsrechte am Werk notwendig ist. 3. Zugänglichmachung des Quellcodes -Der Lizenzgeber kann das Werk entweder als Quellcode oder als ausführbaren Code zur Verfügung stellen. Stellt er es als -ausführbaren Code zur Verfügung, so stellt er darüber hinaus eine maschinenlesbare Kopie des Quellcodes für jedes von -ihm verbreitete Vervielfältigungsstück des Werks zur Verfügung, oder er verweist in einem Vermerk im Anschluss an den -dem Werk beigefügten Urheberrechtshinweis auf einen Speicherort, an dem problemlos und unentgeltlich auf den -Quellcode zugegriffen werden kann, solange der Lizenzgeber das Werk verbreitet oder zugänglich macht. +Der Lizenzgeber kann das Werk entweder als Quellcode oder als ausführbaren Code zur Verfügung stellen. Stellt er es als ausführbaren Code zur Verfügung, so stellt er darüber hinaus eine maschinenlesbare Kopie des Quellcodes für jedes von ihm verbreitete Vervielfältigungsstück des Werks zur Verfügung, oder er verweist in einem Vermerk im Anschluss an den dem Werk beigefügten Urheberrechtshinweis auf einen Speicherort, an dem problemlos und unentgeltlich auf den Quellcode zugegriffen werden kann, solange der Lizenzgeber das Werk verbreitet oder zugänglich macht. 4. Einschränkungen des Urheberrechts -Es ist nicht Zweck dieser Lizenz, Ausnahmen oder Schranken der ausschließlichen Rechte des Urhebers am Werk, die -dem Lizenznehmer zugutekommen, einzuschränken. Auch die Erschöpfung dieser Rechte bleibt von dieser Lizenz -unberührt. +Es ist nicht Zweck dieser Lizenz, Ausnahmen oder Schranken der ausschließlichen Rechte des Urhebers am Werk, die dem Lizenznehmer zugutekommen, einzuschränken. Auch die Erschöpfung dieser Rechte bleibt von dieser Lizenz . 5. Pflichten des Lizenznehmers -Die Einräumung der oben genannten Rechte ist an mehrere Beschränkungen und Pflichten für den Lizenznehmer -gebunden: +Die Einräumung der oben genannten Rechte ist an mehrere Beschränkungen und Pflichten für den Lizenznehmer gebunden: + +Urheberrechtshinweis, Lizenztext, Nennung des Bearbeiters: Der Lizenznehmer muss alle Urheberrechts-, Patent- oder Markenrechtshinweise und alle Hinweise auf die Lizenz und den Haftungsausschluss unverändert lassen. Jedem von ihm verbreiteten oder zugänglich gemachten Vervielfältigungsstück des Werks muss der Lizenznehmer diese Hinweise sowie diese Lizenz beifügen. Der Lizenznehmer muss auf jedem abgeleiteten Werk deutlich darauf hinweisen, dass das geändert wurde, und das Datum der Bearbeitung angeben. + +»Copyleft«-Klausel: Der Lizenznehmer darf Vervielfältigungen des Originalwerks oder Bearbeitungen nur unter den Bedingungen dieser EUPL oder einer neueren Version dieser Lizenz verbreiten oder zugänglich machen, außer wenn das Originalwerk ausdrücklich nur unter dieser Lizenzversion - z. B. mit der Angabe »Nur EUPL V. 1.2« - verbreitet werden darf. Der Lizenznehmer (der zum Lizenzgeber wird) darf für das Werk oder die Bearbeitung keine zusätzlichen Bedingungen anbieten oder vorschreiben, die die Bedingungen dieser Lizenz verändern oder einschränken. -Urheberrechtshinweis, Lizenztext, Nennung des Bearbeiters: Der Lizenznehmer muss alle Urheberrechts-, Patent- -oder Markenrechtshinweise und alle Hinweise auf die Lizenz und den Haftungsausschluss unverändert lassen. Jedem von -ihm verbreiteten oder zugänglich gemachten Vervielfältigungsstück des Werks muss der Lizenznehmer diese Hinweise -sowie diese Lizenz beifügen. Der Lizenznehmer muss auf jedem abgeleiteten Werk deutlich darauf hinweisen, dass das -Werk geändert wurde, und das Datum der Bearbeitung angeben. - -»Copyleft«-Klausel: Der Lizenznehmer darf Vervielfältigungen des Originalwerks oder Bearbeitungen nur unter den -Bedingungen dieser EUPL oder einer neueren Version dieser Lizenz verbreiten oder zugänglich machen, außer wenn das -Originalwerk ausdrücklich nur unter dieser Lizenzversion - z. B. mit der Angabe »Nur EUPL V. 1.2« - verbreitet -werden darf. Der Lizenznehmer (der zum Lizenzgeber wird) darf für das Werk oder die Bearbeitung keine zusätzlichen -Bedingungen anbieten oder vorschreiben, die die Bedingungen dieser Lizenz verändern oder einschränken. - -Kompatibilitäts-Klausel: Wenn der Lizenznehmer Bearbeitungen, die auf dem Werk und einem anderen Werk, das -unter einer kompatiblen Lizenz lizenziert wurde, basieren, oder die Kopien dieser Bearbeitungen verbreitet oder -zugänglich macht, kann dies unter den Bedingungen dieser kompatiblen Lizenz erfolgen. Unter »kompatibler Lizenz« ist -eine im Anhang dieser Lizenz angeführte Lizenz zu verstehen. Sollten die Verpflichtungen des Lizenznehmers aus der -kompatiblen Lizenz mit denjenigen aus der vorliegenden Lizenz (EUPL) in Konflikt stehen, werden die Verpflichtungen -aus der kompatiblen Lizenz Vorrang haben. - -Bereitstellung des Quellcodes: Wenn der Lizenznehmer Vervielfältigungsstücke des Werks verbreitet oder zugänglich -macht, muss er eine maschinenlesbare Fassung des Quellcodes mitliefern oder einen Speicherort angeben, über den -problemlos und unentgeltlich so lange auf diesen Quellcode zugegriffen werden kann, wie der Lizenznehmer das Werk -verbreitet oder zugänglich macht. - -Rechtsschutz: Diese Lizenz erlaubt nicht die Benutzung von Kennzeichen, Marken oder geschützten Namensrechten des -Lizenzgebers, soweit dies nicht für die angemessene und übliche Beschreibung der Herkunft des Werks und der -inhaltlichen Wiedergabe des Urheberrechtshinweises erforderlich ist. +Kompatibilitäts-Klausel: Wenn der Lizenznehmer Bearbeitungen, die auf dem Werk und einem anderen Werk, das unter einer kompatiblen Lizenz lizenziert wurde, basieren, oder die Kopien dieser Bearbeitungen verbreitet oder zugänglich macht, kann dies unter den Bedingungen dieser kompatiblen Lizenz erfolgen. Unter »kompatibler Lizenz« ist eine im Anhang dieser Lizenz angeführte Lizenz zu verstehen. Sollten die Verpflichtungen des Lizenznehmers aus der kompatiblen Lizenz mit denjenigen aus der vorliegenden Lizenz (EUPL) in Konflikt stehen, werden die Verpflichtungen aus der kompatiblen Lizenz Vorrang haben. + +Bereitstellung des Quellcodes: Wenn der Lizenznehmer Vervielfältigungsstücke des Werks verbreitet oder zugänglich macht, muss er eine maschinenlesbare Fassung des Quellcodes mitliefern oder einen Speicherort angeben, über den problemlos und unentgeltlich so lange auf diesen Quellcode zugegriffen werden kann, wie der Lizenznehmer das Werk verbreitet oder zugänglich macht. + +Rechtsschutz: Diese Lizenz erlaubt nicht die Benutzung von Kennzeichen, Marken oder geschützten Namensrechten des Lizenzgebers, soweit dies nicht für die angemessene und übliche Beschreibung der Herkunft des Werks und der inhaltlichen Wiedergabe des Urheberrechtshinweises erforderlich ist. 6. Urheber und Bearbeiter -Der ursprüngliche Lizenzgeber gewährleistet, dass er das Urheberrecht am Originalwerk innehat oder dieses an ihn -lizenziert wurde und dass er befugt ist, diese Lizenz zu erteilen. +Der ursprüngliche Lizenzgeber gewährleistet, dass er das Urheberrecht am Originalwerk innehat oder dieses an ihn lizenziert wurde und dass er befugt ist, diese Lizenz zu erteilen. -Jeder Bearbeiter gewährleistet, dass er das Urheberrecht an den von ihm vorgenommenen Änderungen des Werks besitzt -und befugt ist, diese Lizenz zu erteilen. +Jeder Bearbeiter gewährleistet, dass er das Urheberrecht an den von ihm vorgenommenen Änderungen des Werks besitzt und befugt ist, diese Lizenz zu erteilen. -Jedes Mal, wenn Sie die Lizenz annehmen, erteilen Ihnen der ursprüngliche Lizenzgeber und alle folgenden Bearbeiter -eine Befugnis zur Nutzung ihrer Beiträge zum Werk unter den Bedingungen dieser Lizenz. +Jedes Mal, wenn Sie die Lizenz annehmen, erteilen Ihnen der ursprüngliche Lizenzgeber und alle folgenden Bearbeiter eine Befugnis zur Nutzung ihrer Beiträge zum Werk unter den Bedingungen dieser Lizenz. 7. Gewährleistungsausschluss -Die Arbeit an diesem Werk wird laufend fortgeführt; es wird durch unzählige Bearbeiter ständig verbessert. Das Werk ist -nicht vollendet und kann daher Fehler (»bugs«) enthalten, die dieser Art der Entwicklung inhärent sind. +Die Arbeit an diesem Werk wird laufend fortgeführt; es wird durch unzählige Bearbeiter ständig verbessert. Das Werk ist nicht vollendet und kann daher Fehler (»bugs«) enthalten, die dieser Art der Entwicklung inhärent sind. -Aus den genannten Gründen wird das Werk unter dieser Lizenz »so, wie es ist« ohne jegliche Gewährleistung zur -Verfügung gestellt. Dies gilt unter anderem - aber nicht ausschließlich - für Marktreife, Verwendbarkeit für einen -bestimmten Zweck, Mängelfreiheit, Richtigkeit sowie Nichtverletzung von anderen Immaterialgüterrechten als dem -Urheberrecht (vgl. dazu Artikel 6 dieser Lizenz). +Aus den genannten Gründen wird das Werk unter dieser Lizenz »so, wie es ist« ohne jegliche Gewährleistung zur Verfügung gestellt. Dies gilt unter anderem - aber nicht ausschließlich - für Marktreife, Verwendbarkeit für einen bestimmten Zweck, Mängelfreiheit, Richtigkeit sowie Nichtverletzung von anderen Immaterialgüterrechten als dem Urheberrecht (vgl. dazu Artikel 6 dieser Lizenz). -Dieser Gewährleistungsausschluss ist wesentlicher Bestandteil der Lizenz und Bedingung für die Einräumung von -Rechten an dem Werk. +Dieser Gewährleistungsausschluss ist wesentlicher Bestandteil der Lizenz und Bedingung für die Einräumung von Rechten an dem Werk. 8. Haftungsausschluss/Haftungsbeschränkung -Außer in Fällen von Vorsatz oder der Verursachung von Personenschäden haftet der Lizenzgeber nicht für direkte oder -indirekte, materielle oder immaterielle Schäden irgendwelcher Art, die aus der Lizenz oder der Benutzung des Werks -folgen; dies gilt unter anderem, aber nicht ausschließlich, für Firmenwertverluste, Produktionsausfall, Computerausfall -oder Computerfehler, Datenverlust oder wirtschaftliche Schäden, und zwar auch dann, wenn der Lizenzgeber auf die -Möglichkeit solcher Schäden hingewiesen wurde. Unabhängig davon haftet der Lizenzgeber im Rahmen der gesetzlichen -Produkthaftung, soweit die entsprechenden Regelungen auf das Werk anwendbar sind. +Außer in Fällen von Vorsatz oder der Verursachung von Personenschäden haftet der Lizenzgeber nicht für direkte oder indirekte, materielle oder immaterielle Schäden irgendwelcher Art, die aus der Lizenz oder der Benutzung des Werks folgen; dies gilt unter anderem, aber nicht ausschließlich, für Firmenwertverluste, Produktionsausfall, Computerausfall oder Computerfehler, Datenverlust oder wirtschaftliche Schäden, und zwar auch dann, wenn der Lizenzgeber auf die Möglichkeit solcher Schäden hingewiesen wurde. Unabhängig davon haftet der Lizenzgeber im Rahmen der gesetzlichen Produkthaftung, soweit die entsprechenden Regelungen auf das Werk anwendbar sind. 9. Zusatzvereinbarungen -Wenn Sie das Werk verbreiten, können Sie Zusatzvereinbarungen schließen, in denen Verpflichtungen oder -Dienstleistungen festgelegt werden, die mit dieser Lizenz vereinbar sind. Sie dürfen Verpflichtungen indessen nur in -Ihrem eigenen Namen und auf Ihre eigene Verantwortung eingehen, nicht jedoch im Namen des ursprünglichen -Lizenzgebers oder eines anderen Bearbeiters, und nur, wenn Sie sich gegenüber allen Bearbeitern verpflichten, sie zu -entschädigen, zu verteidigen und von der Haftung freizustellen, falls aufgrund der von Ihnen eingegangenen -Gewährleistungsverpflichtung oder Haftungsübernahme Forderungen gegen sie geltend gemacht werden oder eine -Haftungsverpflichtung entsteht. +Wenn Sie das Werk verbreiten, können Sie Zusatzvereinbarungen schließen, in denen Verpflichtungen oder Dienstleistungen festgelegt werden, die mit dieser Lizenz vereinbar sind. Sie dürfen Verpflichtungen indessen nur in Ihrem eigenen Namen und auf Ihre eigene Verantwortung eingehen, nicht jedoch im Namen des ursprünglichen Lizenzgebers oder eines anderen Bearbeiters, und nur, wenn Sie sich gegenüber allen Bearbeitern verpflichten, sie zu entschädigen, zu verteidigen und von der Haftung freizustellen, falls aufgrund der von Ihnen eingegangenen Gewährleistungsverpflichtung oder Haftungsübernahme Forderungen gegen sie geltend gemacht werden oder eine Haftungsverpflichtung entsteht. 10. Annahme der Lizenz -Sie können den Bestimmungen dieser Lizenz zustimmen, indem Sie das Symbol »Lizenz annehmen« unter dem Fenster -mit dem Lizenztext anklicken oder indem Sie Ihre Zustimmung auf vergleichbare Weise in einer nach anwendbarem -Recht zulässigen Form geben. Das Anklicken des Symbols gilt als Anzeichen Ihrer eindeutigen und unwiderruflichen -Annahme der Lizenz und der darin enthaltenen Klauseln und Bedingungen. - -In gleicher Weise gilt als Zeichen der eindeutigen und unwiderruflichen Zustimmung die Ausübung eines Rechtes, das in -Artikel 2 dieser Lizenz angeführt ist, wie das Erstellen einer Bearbeitung oder die Verbreitung oder Zugänglichmachung -des Werks oder dessen Vervielfältigungen. +Sie können den Bestimmungen dieser Lizenz zustimmen, indem Sie das Symbol »Lizenz annehmen« unter dem Fenster mit dem Lizenztext anklicken oder indem Sie Ihre Zustimmung auf vergleichbare Weise in einer nach anwendbarem Recht zulässigen Form geben. Das Anklicken des Symbols gilt als Anzeichen Ihrer eindeutigen und unwiderruflichen Annahme der Lizenz und der darin enthaltenen Klauseln und Bedingungen. + +In gleicher Weise gilt als Zeichen der eindeutigen und unwiderruflichen Zustimmung die Ausübung eines Rechtes, das in Artikel 2 dieser Lizenz angeführt ist, wie das Erstellen einer Bearbeitung oder die Verbreitung oder Zugänglichmachung des Werks oder dessen Vervielfältigungen. 11. Informationspflichten -Wenn Sie das Werk verbreiten oder zugänglich machen (beispielsweise, indem Sie es zum Herunterladen von einer -Website anbieten), müssen Sie über den Vertriebskanal oder das benutzte Verbreitungsmedium der Öffentlichkeit -zumindest jene Informationen bereitstellen, die nach dem anwendbaren Recht bezüglich der Lizenzgeber, der Lizenz und -ihrer Zugänglichkeit, des Abschlusses des Lizenzvertrags sowie darüber, wie die Lizenz durch den Lizenznehmer -gespeichert und vervielfältigt werden kann, erforderlich sind. +Wenn Sie das Werk verbreiten oder zugänglich machen (beispielsweise, indem Sie es zum Herunterladen von einer Website anbieten), müssen Sie über den Vertriebskanal oder das benutzte Verbreitungsmedium der Öffentlichkeit zumindest jene Informationen bereitstellen, die nach dem anwendbaren Recht bezüglich der Lizenzgeber, der Lizenz und ihrer Zugänglichkeit, des Abschlusses des Lizenzvertrags sowie darüber, wie die Lizenz durch den Lizenznehmer gespeichert und vervielfältigt werden kann, erforderlich sind. 12. Beendigung der Lizenz -Die Lizenz und die damit eingeräumten Rechte erlöschen automatisch, wenn der Lizenznehmer gegen die Lizenzbedingungen -verstößt. +Die Lizenz und die damit eingeräumten Rechte erlöschen automatisch, wenn der Lizenznehmer gegen die Lizenzbedingungen verstößt. -Ein solches Erlöschen der Lizenz führt nicht zum Erlöschen der Lizenzen von Personen, denen das Werk vom -Lizenznehmer unter dieser Lizenz zur Verfügung gestellt worden ist, solange diese Personen die Lizenzbedingungen -erfüllen. +Ein solches Erlöschen der Lizenz führt nicht zum Erlöschen der Lizenzen von Personen, denen das Werk vom Lizenznehmer unter dieser Lizenz zur Verfügung gestellt worden ist, solange diese Personen die Lizenzbedingungen erfüllen. 13. Sonstiges Unbeschadet des Artikels 9 stellt die Lizenz die vollständige Vereinbarung der Parteien über das Werk dar. -Sind einzelne Bestimmungen der Lizenz nach geltendem Recht nichtig oder unwirksam, so berührt dies nicht die -Wirksamkeit oder Durchsetzbarkeit der Lizenz an sich. Solche Bestimmungen werden vielmehr so ausgelegt oder -modifiziert, dass sie wirksam und durchsetzbar sind. - -Die Europäische Kommission kann weitere Sprachfassungen oder neue Versionen dieser Lizenz oder aktualisierte -Fassungen des Anhangs veröffentlichen, soweit dies notwendig und angemessen ist, ohne den Umfang der Lizenzrechte -zu verringern. Neue Versionen werden mit einer eindeutigen Versionsnummer veröffentlicht. +Sind einzelne Bestimmungen der Lizenz nach geltendem Recht nichtig oder unwirksam, so berührt dies nicht die Wirksamkeit oder Durchsetzbarkeit der Lizenz an sich. Solche Bestimmungen werden vielmehr so ausgelegt oder modifiziert, dass sie wirksam und durchsetzbar sind. -Alle von der Europäischen Kommission anerkannten Sprachfassungen dieser Lizenz sind gleichwertig. Die Parteien -können sich auf die Sprachfassung ihrer Wahl berufen. +Die Europäische Kommission kann weitere Sprachfassungen oder neue Versionen dieser Lizenz oder aktualisierte Fassungen des Anhangs veröffentlichen, soweit dies notwendig und angemessen ist, ohne den Umfang der Lizenzrechte zu verringern. Neue Versionen werden mit einer eindeutigen Versionsnummer veröffentlicht. + +Alle von der Europäischen Kommission anerkannten Sprachfassungen dieser Lizenz sind gleichwertig. Die Parteien können sich auf die Sprachfassung ihrer Wahl berufen. 14. Gerichtsstand Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: -- Für alle Streitigkeiten über die Auslegung dieser Lizenz zwischen den Organen, Einrichtungen und sonstigen Stellen - der Europäischen Union als Lizenzgeber und einem Lizenznehmer ist der Gerichtshof der Europäischen Union - gemäß Artikel 272 des Vertrags über die Arbeitsweise der Europäischen Union zuständig; +- Für alle Streitigkeiten über die Auslegung dieser Lizenz zwischen den Organen, Einrichtungen und sonstigen Stellen der Europäischen Union als Lizenzgeber und einem Lizenznehmer ist der Gerichtshof der Europäischen Union gemäß Artikel 272 des Vertrags über die Arbeitsweise der Europäischen Union zuständig; -- Gerichtsstand für Streitigkeiten zwischen anderen Parteien über die Auslegung dieser Lizenz ist allein der Ort, an - dem der Lizenzgeber seinen Wohnsitz oder den wirtschaftlichen Mittelpunkt seiner Tätigkeit hat. +- Gerichtsstand für Streitigkeiten zwischen anderen Parteien über die Auslegung dieser Lizenz ist allein der Ort, an dem der Lizenzgeber seinen Wohnsitz oder den wirtschaftlichen Mittelpunkt seiner Tätigkeit hat. 15. Anwendbares Recht Unbeschadet besonderer Vereinbarungen zwischen den Parteien gilt Folgendes: -- Diese Lizenz unterliegt dem Recht des Mitgliedstaats der Europäischen Union, in dem der Lizenzgeber seinen Sitz, - Wohnsitz oder eingetragenen Sitz hat; +- Diese Lizenz unterliegt dem Recht des Mitgliedstaats der Europäischen Union, in dem der Lizenzgeber seinen Sitz, Wohnsitz oder eingetragenen Sitz hat; -- diese Lizenz unterliegt dem belgischen Recht, wenn der Lizenzgeber keinen Sitz, Wohnsitz oder eingetragenen Sitz in - einem Mitgliedstaat der Europäischen Union hat. +- diese Lizenz unterliegt dem belgischen Recht, wenn der Lizenzgeber keinen Sitz, Wohnsitz oder eingetragenen Sitz in einem Mitgliedstaat der Europäischen Union hat. Anlage @@ -299,12 +212,9 @@ - Québec Free and Open-Source Licence - Reciprocity (LiLiQ-R) oder Strong Reciprocity (LiLiQ-R+) -- Die Europäische Kommission kann diesen Anhang aktualisieren, um neuere Fassungen der obigen Lizenzen - aufzunehmen, ohne hierfür eine neue Fassung der EUPL auszuarbeiten, solange diese Lizenzen die in Artikel 2 - gewährten Rechte gewährleisten und den erfassten Quellcode vor ausschließlicher Aneignung schützen. +- Die Europäische Kommission kann diesen Anhang aktualisieren, um neuere Fassungen der obigen Lizenzen aufzunehmen, ohne hierfür eine neue Fassung der EUPL auszuarbeiten, solange diese Lizenzen die in Artikel 2 gewährten Rechte gewährleisten und den erfassten Quellcode vor ausschließlicher Aneignung schützen. -- Alle sonstigen Änderungen oder Ergänzungen dieses Anhangs bedürfen der Ausarbeitung einer neuen Version der - EUPL. +- Alle sonstigen Änderungen oder Ergänzungen dieses Anhangs bedürfen der Ausarbeitung einer neuen Version der EUPL. @@ -318,12 +228,12 @@ OpenSSL Lizenz: Apache 2.0 - Version: 3.4.1 + Version: 3.5.4 Adresse: https://www.openssl.org/ Qt Lizenz: LGPL v3 - Version: 6.8.1 + Version: 6.9.2 Adresse: https://www.qt.io/ http_parser @@ -331,11 +241,6 @@ Version: 2.9.4 Adresse: https://github.com/nodejs/http-parser/ -AndroidX Core Library - Lizenz: Apache 2.0 - Version: 1.9.0 - Adresse: https://developer.android.com/jetpack/androidx - Die oben genannten Lizenztexte lauten in ihrer originalen Fassung wie folgt: @@ -347,10 +252,7 @@ The Qt Toolkit is Copyright (C) 2016 The Qt Company Ltd. Contact: http://www.qt.io/licensing/ -You may use, distribute and copy the Qt Toolkit under the terms of -GNU Lesser General Public License version 3, which is displayed below. -This license makes reference to the version 3 of the GNU General -Public License, which you can find in the LICENSE.GPLv3 file. +You may use, distribute and copy the Qt Toolkit under the terms of GNU Lesser General Public License version 3, which is displayed below. This license makes reference to the version 3 of the GNU General Public License, which you can find in the LICENSE.GPLv3 file. ------------------------------------------------------------------------- @@ -358,164 +260,77 @@ Version 3, 29 June 2007 Copyright © 2007 Free Software Foundation, Inc. -Everyone is permitted to copy and distribute verbatim copies of this -licensedocument, but changing it is not allowed. +Everyone is permitted to copy and distribute verbatim copies of this licensedocument, but changing it is not allowed. -This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. +This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. -As used herein, «this License» refers to version 3 of the GNU Lesser -General Public License, and the «GNU GPL» refers to version 3 of the -GNU General Public License. - -«The Library» refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - -An «Application» is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - -A «Combined Work» is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the «Linked -Version». - -The «Minimal Corresponding Source» for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - -The «Corresponding Application Code» for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. +As used herein, «this License» refers to version 3 of the GNU Lesser General Public License, and the «GNU GPL» refers to version 3 of the GNU General Public License. + +«The Library» refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. + +An «Application» is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. + +A «Combined Work» is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the «Linked Version». + +The «Minimal Corresponding Source» for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. + +The «Corresponding Application Code» for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. -You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. +You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. -If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort - to ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or +If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: + + a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. + b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. -The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that - the Library is used in it and that the Library and its use are - covered by this License. +The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: - b) Accompany the object code with a copy of the GNU GPL and this - license document. + a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. -You may convey a Combined Work under terms of your choice that, taken -together, effectively do not restrict modification of the portions of -the Library contained in the Combined Work and reverse engineering for -debugging such modifications, if you also do each of the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this - license document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. +You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license document. + + c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: - 0) Convey the Minimal Corresponding Source under the terms of - this License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with - the Library. A suitable mechanism is one that (a) uses at run - time a copy of the Library already present on the user's - computer system, and (b) will operate properly with a modified - version of the Library that is interface-compatible with the - Linked Version. - - e) Provide Installation Information, but only if you would - otherwise be required to provide such information under section 6 - of the GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the Application - with a modified version of the Linked Version. (If you use option - 4d0, the Installation Information must accompany the Minimal - Corresponding Source and Corresponding Application Code. If you - use option 4d1, you must provide the Installation Information in - the manner specified by section 6 of the GNU GPL for conveying - Corresponding Source.) + 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. + + e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. -You may place library facilities that are a work based on the Library -side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities, conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of - it is a work based on the Library, and explaining where to find - the accompanying uncombined form of the same work. +You may place library facilities that are a work based on the Library by side in a single library together with other library that are not Applications and are not covered by this , and convey such a combined library under terms of your , if you do both of the following: + + a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. -The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -as you received it specifies that a certain numbered version of the -GNU Lesser General Public License «or any later version» applies to -it, you have the option of following the terms and conditions either -of that published version or of any later version published by the -Free Software Foundation. If the Library as you received it does not -specify a version number of the GNU Lesser General Public License, -you may choose any version of the GNU Lesser General Public License -ever published by the Free Software Foundation. - -If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the Library. +The Free Software Foundation may publish revised and/or new versions the GNU Lesser General Public License from time to time. Such new will be similar in spirit to the present version, but may in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library you received it specifies that a certain numbered version of the Lesser General Public License «or any later version» applies to , you have the option of following the terms and conditions either that published version or of any later version published by the Software Foundation. If the Library as you received it does not a version number of the GNU Lesser General Public License, may choose any version of the GNU Lesser General Public License published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide future versions of the GNU Lesser General Public License shall , that proxy's public statement of acceptance of any version is authorization for you to choose that version for the Library. @@ -526,23 +341,11 @@ Copyright Joyent, Inc. and other Node contributors. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. +Permission is hereby granted, free of charge, to any person obtaining a copy this software and associated documentation files (the "Software"), to in the Software without restriction, including without limitation the to use, copy, modify, merge, publish, distribute, sublicense, and/or copies of the Software, and to permit persons to whom the Software is to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER , WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS THE SOFTWARE. @@ -557,172 +360,50 @@ 1. Definitions. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - END OF TERMS AND CONDITIONS + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff -Nru ausweisapp2-2.3.1/README.rst ausweisapp2-2.4.0/README.rst --- ausweisapp2-2.3.1/README.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/README.rst 2025-10-30 10:10:48.000000000 +0000 @@ -36,7 +36,7 @@ CMake erkennt während der Konfigurationszeit automatisch die Abhängigkeiten. Dazu kann die Variable *CMAKE_PREFIX_PATH* verwendet werden, um die Toolchain CMake -bekannt zu machen. Alternativ zu `%PATH%` bzw. `$PATH` können alle Ordner, die dort +bekannt zu machen. Alternativ zu ``%PATH%`` bzw. ``$PATH`` können alle Ordner, die dort für den Build eingetragen wurden, über diesen Mechanismus an CMake übergeben werden. Als Generator für Makefiles sollte unter Windows für MinGW "MinGW Makefiles" und @@ -67,7 +67,6 @@ -- Detecting CXX compile features - done -- VENDOR: Governikus GmbH & Co. KG -- VERSION: 1.24.0 - -- Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE) -- Found Java: C:/Program Files/Java/jre1.8.0_241/bin/java.exe (found version "1.8.0.241") found components: Runtime -- Found Hg: C:/Program Files/TortoiseHg/hg.exe (found version "5.6.1") -- DVCS tag: tip @@ -156,8 +155,7 @@ -------- Nachdem die Build-Umgebung eingerichtet ist, kann je nach System ein Package erstellt werden. -- Unter Windows ist hierfür noch das WiX Toolset (http://wixtoolset.org/ Getestet: 3.8 bis 3.10) - notwendig. +- Unter Windows ist hierfür das WiX Toolset (http://wixtoolset.org/ Getestet: 6.0.0) notwendig. Mittels "mingw32-make package" wird die AusweisApp gebaut und ein MSI Paket bereitgestellt. - Unter macOS wird mittels "make package" die AusweisApp gebaut und ein DMG bereitgestellt. diff -Nru ausweisapp2-2.3.1/appveyor.yml ausweisapp2-2.4.0/appveyor.yml --- ausweisapp2-2.3.1/appveyor.yml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/appveyor.yml 2025-10-30 10:10:48.000000000 +0000 @@ -16,71 +16,70 @@ ARCHI: amd64 configuration: - - Release - #- Debug + - Release + # - Debug install: - - if "%PlatformToolset%"=="mingw-w64" set PATH=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-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% - - if "%PlatformToolset%"=="v142" call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %archi% - - if "%PlatformToolset%"=="v143" call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" %archi% + - if "%PlatformToolset%"=="mingw-w64" set PATH=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-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% + - if "%PlatformToolset%"=="v142" call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %archi% + - if "%PlatformToolset%"=="v143" call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" %archi% build: - verbosity: minimal + verbosity: minimal before_build: -- ps: | - Write-Output "Configuration: $env:CONFIGURATION" - Write-Output "Platform: $env:PLATFORM" - $generator = switch ($env:PLATFORMTOOLSET) - { - "v143" {"Visual Studio 17 2022"} - "v142" {"Visual Studio 16 2019"} - "v141" {"Visual Studio 15 2017"} - "mingw-w64" {"MinGW Makefiles"} - "mingw-w64_i686" {"MinGW Makefiles"} - } - $cmake_generator_architecture = switch ($env:PLATFORM) - { - "x86" {"Win32"} - "x64" {"x64"} - "mingw-w64" {""} - "mingw-w64_i686" {""} - } + - ps: | + Write-Output "Configuration: $env:CONFIGURATION" + Write-Output "Platform: $env:PLATFORM" + $generator = switch ($env:PLATFORMTOOLSET) + { + "v143" {"Visual Studio 17 2022"} + "v142" {"Visual Studio 16 2019"} + "v141" {"Visual Studio 15 2017"} + "mingw-w64" {"MinGW Makefiles"} + "mingw-w64_i686" {"MinGW Makefiles"} + } + $cmake_generator_architecture = switch ($env:PLATFORM) + { + "x86" {"Win32"} + "x64" {"x64"} + "mingw-w64" {""} + "mingw-w64_i686" {""} + } build_script: - - cd "%APPVEYOR_BUILD_FOLDER%" - - set OPENSSL_ROOT=%OPENSSLPath% - - set PATH=%PATH%;%QTPATH%;%QTPATH%/bin;%OPENSSLPath% - - echo %PATH% - - echo %OPENSSL_ROOT% - - mkdir _build - - cd _build - - - ps: | - if($cmake_generator_architecture) { - cmake -G "$generator" -A $cmake_generator_architecture -DCMAKE_BUILD_TYPE="$env:CONFIGURATION" .. - } else { - cmake -G "$generator" -DCMAKE_BUILD_TYPE="$env:CONFIGURATION" .. - } - if ($LastExitCode -ne 0) { - throw "Exec: $ErrorMessage" - } - & cmake --build . --target package --config $env:CONFIGURATION - if ($LastExitCode -ne 0) { - throw "Exec: $ErrorMessage" - } + - cd "%APPVEYOR_BUILD_FOLDER%" + - set OPENSSL_ROOT=%OPENSSLPath% + - set PATH=%PATH%;%QTPATH%;%QTPATH%/bin;%OPENSSLPath% + - echo %PATH% + - echo %OPENSSL_ROOT% + - mkdir _build + - cd _build + + - ps: | + if($cmake_generator_architecture) { + cmake -G "$generator" -A $cmake_generator_architecture -DCMAKE_BUILD_TYPE="$env:CONFIGURATION" .. + } else { + cmake -G "$generator" -DCMAKE_BUILD_TYPE="$env:CONFIGURATION" .. + } + if ($LastExitCode -ne 0) { + throw "Exec: $ErrorMessage" + } + & cmake --build . --target package --config $env:CONFIGURATION + if ($LastExitCode -ne 0) { + throw "Exec: $ErrorMessage" + } test_script: - - cd "%APPVEYOR_BUILD_FOLDER%"/_build - - set PATH=%PATH%;%QTPATH%;%QTPATH%/bin - - echo %PATH% - - ctest --output-on-failure -C "%CONFIGURATION%" + - cd "%APPVEYOR_BUILD_FOLDER%"/_build + - set PATH=%PATH%;%QTPATH%;%QTPATH%/bin + - echo %PATH% + - ctest --output-on-failure -C "%CONFIGURATION%" on_finish: - - cd "%APPVEYOR_BUILD_FOLDER%" - - - ps: | - $version = Select-String -Path .\CMakeLists.txt -Pattern "AusweisApp VERSION ([0-9.]+)" | % { $_.Matches[0].Groups[1].Value } - Update-AppveyorBuild -Version "$version-$env:APPVEYOR_BUILD_NUMBER" + - cd "%APPVEYOR_BUILD_FOLDER%" + - ps: | + $version = Select-String -Path .\CMakeLists.txt -Pattern "AusweisApp VERSION ([0-9.]+)" | % { $_.Matches[0].Groups[1].Value } + Update-AppveyorBuild -Version "$version-$env:APPVEYOR_BUILD_NUMBER" diff -Nru ausweisapp2-2.3.1/ci/check_formatting.sh ausweisapp2-2.4.0/ci/check_formatting.sh --- ausweisapp2-2.3.1/ci/check_formatting.sh 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/check_formatting.sh 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,88 @@ +#!/bin/sh + +set -e + +if [ -z "$CI" ]; then + echo "Warning: This script should be called on CI only" + exit 1 +fi + +PATCH=$1 + +REVISION_CURRENT=$(hg id -i) +echo "REVISION_CURRENT: ${REVISION_CURRENT}" + +format_repository() { + cmake --build ../build --target rebuild_cache + cmake --build ../build --target format + cmake --build ../build --target update.translations +} + +# Check if the current repository state is formatted +format_repository +STATUS_CURRENT=$(hg status | wc -c) +if [ ! "0" = "$STATUS_CURRENT" ]; then + echo 'Current repository state is not formatted!' + hg addremove + hg diff + hg commit -m "fix formatting" -s -u 'CI' +fi +REVISION_FORMATTED=$(hg id -i) +echo "REVISION_FORMATTED: ${REVISION_FORMATTED}" + +if [ ! -f "$PATCH" ] && [ -n "$NO_PATCH" ]; then + if [ ! "0" = "$STATUS_CURRENT" ]; then + echo 'FORMATTING FAILED' + else + echo 'FORMATTING SUCCEEDED' + fi + exit 0 +fi + +# Rollback any changes +echo "Rollback to ${REVISION_CURRENT}" +hg update -C -r "$REVISION_CURRENT" + +# Apply patch on the current repository state +if ! hg --config patch.eol=auto --config phases.new-commit=secret import -m 'jenkins patch formatting' -d 'today' -u 'CI' "$PATCH"; then + echo 'FORMATTING FAILED: Patch cannot be applied' + exit 0 +fi + +# Check if the repository state is formatted after the patch +format_repository +STATUS_PATCH=$(hg status | wc -c) +if [ "$STATUS_PATCH" = "0" ]; then + if [ ! "0" = "$STATUS_CURRENT" ]; then + echo 'Patch fixes repository formatting.' + fi + echo 'FORMATTING SUCCEEDED' + exit 0 +elif [ ! "0" = "$STATUS_CURRENT" ]; then + echo 'FORMATTING FAILED: Patch is not formatted' + hg diff + hg revert -a -C + exit 0 +fi + +# Checkout formatted repository state +hg update -C -r "$REVISION_FORMATTED" + +# Apply patch on the formatted repository state +if ! hg --config patch.eol=auto --config phases.new-commit=secret import -m 'jenkins patch formatting' -d 'today' -u 'CI' "$PATCH"; then + echo 'FORMATTING FAILED: Patch cannot be applied, because the current repository state is unformatted and the patch conflicts with the formatted repository state without fixing the formatting!' + exit 0 +fi + +# Check if the patch itself is formatted even though is does not fix +# the unformatted repository state +format_repository +STATUS_PATCH=$(hg status | wc -c) +if [ ! "0" = "$STATUS_PATCH" ]; then + echo 'FORMATTING FAILED: Patch is not formatted' + hg diff + hg revert -a -C + exit 0 +fi + +echo 'FORMATTING SUCCEEDED: Patch does not introduce new formatting issues.' diff -Nru ausweisapp2-2.3.1/ci/cmake/Android.cmake ausweisapp2-2.4.0/ci/cmake/Android.cmake --- ausweisapp2-2.3.1/ci/cmake/Android.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Android.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,131 @@ +if("APK" IN_LIST NAMES) + set(APK ON) + set(PRESET ci-android-apk) +elseif(NAME MATCHES "_AAR_" OR NAME MATCHES "_AAR:") + set(AAR ON) + set(PRESET ci-android-aar) +else() + step(${CMAKE_COMMAND} -DDIST_DIR=${T_DIST_DIR} -P ${CMAKE_DIR}/Merge.cmake) + + block() + include(Files) + endblock() + prepare_gpg(GPG_CMD) + + set(files *-sources.jar *.aar *.pom) + foreach(file ${files}) + file(GLOB file "${T_DIST_DIR}/${file}") + hashsum(${file} NO_FILENAME ALGORITHM MD5 SHA1 SHA256) + step(${GPG_CMD} -a --detach-sig -u $ENV{GPG_ID} ${file}) + endforeach() + + cmake_minimum_required(VERSION 3.29) # CMP0159 + + file(GLOB POM "${T_DIST_DIR}/*.pom") + list(LENGTH POM POM_COUNT) + if(NOT POM_COUNT EQUAL 1) + message(FATAL_ERROR "Cannot find unique POM file: ${POM}") + endif() + + function(parse_pom _tag) + file(STRINGS "${POM}" _unused REGEX "<${_tag}>(.+)") + set(${_tag} ${CMAKE_MATCH_1} PARENT_SCOPE) + message(STATUS "Parse ${_tag}: ${CMAKE_MATCH_1}") + endfunction() + + parse_pom(groupId) + parse_pom(artifactId) + parse_pom(version) + + string(REPLACE "." ";" groupId_List "${groupId}") + list(JOIN groupId_List "/" groupId_Dir) + list(GET groupId_List 0 groupId_RootDir) + + set(BUNDLE_DIR ${T_BUILD_DIR}/${groupId_Dir}/${artifactId}/${version}) + step(${CMAKE_COMMAND} -E make_directory ${BUNDLE_DIR}) + + file(GLOB FILES "${T_DIST_DIR}/${artifactId}-*") + foreach(file ${FILES}) + step(${CMAKE_COMMAND} -E copy_if_different ${file} ${BUNDLE_DIR}) + endforeach() + + step(${CMAKE_COMMAND} -E tar cf dist/${artifactId}-${version}.zip --format=zip ${groupId_RootDir} CHDIR ${T_BUILD_DIR}) + + if(DEFINED ENV{JENKINS_HOME} AND RELEASE) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CI_DIR}/Deploy.cmake) + include(AAR) + endif() + return() +endif() + +block() + include(Files) + include(Libraries) +endblock() + +if(REVIEW) + set(PRESET ${PRESET}-review) +endif() + +if(APK AND RELEASE AND DEFINED ENV{GITLAB_CI} AND DEFINED ENV{ANDROID_API_KEY}) + set(API_JSON ${WORKSPACE}/api.json) + download_file(${API_JSON} $ENV{ANDROID_API_KEY}) + find_package(Python REQUIRED) + step(${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/ci/playstore.py --json ${API_JSON} OUTPUT VERSION_CODE) + message(STATUS "Next VERSION_CODE: ${VERSION_CODE}") + set(VERSION_CODE -DANDROID_VERSION_CODE_BASE=${VERSION_CODE}) +endif() +step(${T_CFG} ${VERSION_CODE} --preset ${PRESET}) + +if(APK) + if(REVIEW OR DEFINED ENV{JENKINS_HOME}) + prepare_android_keystore(DEBUG _unused) + step(${T_TARGET} apk) + else() + if(RELEASE) + prepare_android_keystore(RELEASE STORE_APK) + else() + prepare_android_keystore(DEBUG STORE_APK) + endif() + + step(${T_TARGET} apk ENV + QT_ANDROID_KEYSTORE_PATH=${STORE_APK} + QT_ANDROID_KEYSTORE_ALIAS=${STORE_APK_ALIAS} + QT_ANDROID_KEYSTORE_STORE_PASS=${STORE_APK_PSW}) + endif() + + step(${T_TARGET} dump.apk) + + if(REVIEW OR (DAILY AND DEFINED ENV{JENKINS_HOME})) + step(${T_TARGET} aab) + else() + if(RELEASE) + prepare_android_keystore(UPLOAD STORE_AAB) + else() + prepare_android_keystore(DEBUG STORE_AAB) + endif() + + if(DEFINED ENV{JENKINS_HOME}) + set(STORE_AAB $ENV{APK_SIGN_KEYSTORE_UPLOAD}) + set(STORE_AAB_ALIAS $ENV{APK_SIGN_KEYSTORE_ALIAS_UPLOAD}) + set(STORE_AAB_PSW $ENV{APK_SIGN_KEYSTORE_PSW_UPLOAD}) + endif() + + step(${T_TARGET} aab ENV + QT_ANDROID_KEYSTORE_PATH=${STORE_AAB} + QT_ANDROID_KEYSTORE_ALIAS=${STORE_AAB_ALIAS} + QT_ANDROID_KEYSTORE_STORE_PASS=${STORE_AAB_PSW}) + endif() + + step(${T_TARGET} verify.signature) + file(GLOB FILES "${T_DIST_DIR}/*.apk" "${T_DIST_DIR}/*.aab") +elseif(AAR) + step(${T_TARGET} aar) + file(GLOB FILES "${T_DIST_DIR}/*.aar") +endif() + +hashsum(${FILES}) + +if(NOT RELEASE) + step(${T_CTEST}) +endif() diff -Nru ausweisapp2-2.3.1/ci/cmake/Bootstrap.cmake ausweisapp2-2.4.0/ci/cmake/Bootstrap.cmake --- ausweisapp2-2.3.1/ci/cmake/Bootstrap.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Bootstrap.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,34 @@ +set(ENV_FILE ${CMAKE_BINARY_DIR}/env) + +function(append_env_file _line) + file(APPEND "${ENV_FILE}" ${_line}\n) +endfunction() + +step(hg log -r . -T {node} -R ${CMAKE_SOURCE_DIR} OUTPUT REVISION) + +set(IS_FINAL_VERSION false) +if(RELEASE) + message(STATUS "Used REV: ${REV}") + + string(REPLACE "." ";" VERSION_LIST "${REV}") + list(LENGTH VERSION_LIST VERSION_LIST_LENGTH) + if(VERSION_LIST_LENGTH GREATER_EQUAL 3) + list(GET VERSION_LIST 1 PROJECT_VERSION_MINOR) + list(GET VERSION_LIST 2 PROJECT_VERSION_PATCH) + set(PROJECT_VERSION ${REV}) + + list(APPEND CMAKE_MODULE_PATH ${CMAKE_DIR}) + include(DVCS) + list(POP_BACK CMAKE_MODULE_PATH) + if(NOT IS_BETA_VERSION) + set(IS_FINAL_VERSION true) + endif() + endif() +endif() + +append_env_file("RELEASE_FINAL=${IS_FINAL_VERSION}") +append_env_file("RELEASE=${RELEASE}") +append_env_file("REV=${REV}") +append_env_file("REVISION=${REVISION}") + +configure_file("${CMAKE_SOURCE_DIR}/.gitlab-ci-child.yml" "${CMAKE_BINARY_DIR}/gitlab-ci-child.yml" COPYONLY) diff -Nru ausweisapp2-2.3.1/ci/cmake/Configuration.cmake ausweisapp2-2.4.0/ci/cmake/Configuration.cmake --- ausweisapp2-2.3.1/ci/cmake/Configuration.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Configuration.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1 @@ +include(${CMAKE_CI_DIR}/Light.cmake/Configuration.cmake) diff -Nru ausweisapp2-2.3.1/ci/cmake/Container.cmake ausweisapp2-2.4.0/ci/cmake/Container.cmake --- ausweisapp2-2.3.1/ci/cmake/Container.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Container.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,103 @@ +if("VNC" IN_LIST NAMES) + set(VNC ON) + set(TARGET vnc) + set(Dockerfile -f ${CMAKE_SOURCE_DIR}/Dockerfile.vnc) +else() + set(TARGET sdk) +endif() + +if(RELEASE) + if(DEFINED ENV{GITLAB_CI}) + set(TAG $ENV{REV}) + else() + set(TAG $ENV{changeset}) + endif() + set(FILE_TAG ${TAG}) +elseif(REVIEW) + if(DEFINED ENV{GITLAB_CI}) + set(TAG $ENV{CI_JOB_NAME_SLUG}-$ENV{CI_PIPELINE_ID}) + else() + set(TAG $ENV{BUILD_TAG}) + endif() + set(FILE_TAG ${TAG}) +else() + if(DEFINED ENV{GITLAB_CI}) + set(TAG dev-$ENV{REV}) + set(FILE_TAG $ENV{REVISION}) + else() + set(TAG dev-$ENV{MERCURIAL_REVISION_BRANCH}) + set(FILE_TAG $ENV{MERCURIAL_REVISION_SHORT}) + endif() + string(REPLACE "-default" "" TAG "${TAG}") +endif() + +if(NOT EXISTS ${T_BUILD_DIR}) + step(${CMAKE_COMMAND} -E make_directory ${T_BUILD_DIR}) +endif() + +if(DEFINED ENV{GITLAB_CI}) + find_program(CMD buildah REQUIRED) +else() + find_program(CMD docker REQUIRED) +endif() + +if(DAILY AND NOT VNC AND NOT DEFINED ENV{GITLAB_CI}) + step(${CMD} container prune -f) +endif() + +set(IMAGE $ENV{CI_REGISTRY_IMAGE}/${TARGET}:${TAG}) + +step(${CMD} login -u $ENV{CI_REGISTRY_USER} -p $ENV{CI_REGISTRY_PASSWORD} $ENV{CI_REGISTRY}) + +if(DEFINED ENV{GITLAB_CI}) + set(CACHE_REGISTRY $ENV{CI_REGISTRY_IMAGE}/cache/${TARGET}) + + step(${CMD} build + --pull + --layers + --cache-from ${CACHE_REGISTRY} + --cache-to ${CACHE_REGISTRY} + -t ${IMAGE} + --build-arg CCACHE_REMOTE_STORAGE=$ENV{CCACHE_REMOTE_STORAGE} + ${Dockerfile} + ${CMAKE_SOURCE_DIR} + ) + step(${CMD} from --name ${TAG} ${IMAGE}) + step(${CMD} run ${TAG} AusweisApp --help) + step(${CMD} rm ${TAG}) + step(${CMD} push ${IMAGE} docker-archive:${T_BUILD_DIR}/AusweisApp-${FILE_TAG}.tar) +else() + step(${CMD} build + --pull + -t ${IMAGE} + --build-arg CCACHE_REMOTE_STORAGE=$ENV{CCACHE_REMOTE_STORAGE} + ${Dockerfile} + ${CMAKE_SOURCE_DIR} + ) + step(${CMD} run --rm ${IMAGE} AusweisApp --help) + step(${CMD} save -o ${T_BUILD_DIR}/AusweisApp-${FILE_TAG}.tar ${IMAGE}) +endif() + +step(${CMD} inspect ${IMAGE}) + +if(RELEASE OR DAILY) + step(${CMD} push ${IMAGE}) +endif() + + +if(NOT DEFINED ENV{GITLAB_CI}) + step(${CMD} rmi -f ${IMAGE}) + + if(DAILY AND NOT VNC) + step(${CMD} images --filter "dangling=true" -q OUTPUT IMAGES) + if(IMAGES) + string(STRIP "${IMAGES}" IMAGES) + string(REPLACE "\n" ";" IMAGES "${IMAGES}") + list(REVERSE IMAGES) + list(SUBLIST IMAGES 0 50 IMAGES) + foreach(entry ${IMAGES}) + step(${CMD} rmi -f ${entry}) + endforeach() + endif() + endif() +endif() diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/AAR.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/AAR.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/AAR.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/AAR.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,40 @@ +include(Utils) + +if(DEFINED ENV{JENKINS_HOME}) + file(GLOB zipfile "${T_DIST_DIR}/*.zip") +else() + file(GLOB zipfile "${WORKSPACE}/*.zip") +endif() +cmake_path(GET zipfile FILENAME file) + +find_program(CURL curl REQUIRED) +list(APPEND CURL --fail -v --header "Authorization: Bearer $ENV{CENTRAL_TOKEN}") +step(${CURL} --form bundle=@${zipfile} "https://central.sonatype.com/api/v1/publisher/upload?name=${file}&publishingType=USER_MANAGED" OUTPUT deploymentId) +message(STATUS "Deployment ID: ${deploymentId}") + +while(TRUE) + step(${CURL} --request POST "https://central.sonatype.com/api/v1/publisher/status?id=${deploymentId}" OUTPUT output) + string(JSON deploymentState GET "${output}" deploymentState) + + if(deploymentState STREQUAL "VALIDATED" OR deploymentState STREQUAL "PUBLISHED") + message(STATUS "Deployment succeeded: ${deploymentState}") + break() + elseif(deploymentState STREQUAL "FAILED") + string(JSON errors GET "${output}" errors) + message(STATUS "Deployment failed: ${errors}") + set(FAILED ON) + break() + else() + message(STATUS "Deployment processing: ${deploymentState}") + step(${CMAKE_COMMAND} -E sleep 5) + endif() +endwhile() + +check_beta_version(${zipfile} RESULT is_beta) +if(is_beta OR FAILED) + step(${CURL} --request DELETE "https://central.sonatype.com/api/v1/publisher/deployment/${deploymentId}") +endif() + +if(FAILED) + message(FATAL_ERROR "Deployment failed") +endif() diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/APK.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/APK.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/APK.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/APK.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,9 @@ +block() + include(Files) +endblock() + +set(API_JSON ${WORKSPACE}/api.json) +download_file(${API_JSON} $ENV{ANDROID_API_KEY}) +find_package(Python REQUIRED) +file(GLOB apkfiles "${WORKSPACE}/*.apk") +step(${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/ci/playstore.py --json ${API_JSON} --upload ${apkfiles}) diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Allowlist.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Allowlist.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Allowlist.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Allowlist.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,10 @@ +block() + include(Files) +endblock() + +set(TRUSTSTORE ${WORKSPACE}/truststore.pem) +download_file(${TRUSTSTORE} $ENV{TRUSTSTORE_PEM}) +file(GLOB files "${WORKSPACE}/*.msi" "${WORKSPACE}/*.dmg") +foreach(file ${files}) + step(curl --fail -v --cacert ${TRUSTSTORE} --ssl-reqd --ftp-ssl-control -u "$ENV{ALLOWLIST_USER}:$ENV{ALLOWLIST_PSW}" --upload-file ${file} "$ENV{ALLOWLIST_URL}") +endforeach() diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Archive.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Archive.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Archive.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Archive.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,40 @@ +find_program(SMB smbclient REQUIRED) +list(APPEND SMB $ENV{ARCHIVE_URL} -s /dev/null -U $ENV{ARCHIVE_USER}%$ENV{ARCHIVE_PSW}) + +step(${SMB} "-c mkdir $ENV{REV}" OUTPUT output) +if(output MATCHES "NT_STATUS_OBJECT_NAME_COLLISION") + message(FATAL_ERROR "Directory already exists: $ENV{REV}") +elseif(output MATCHES "NT_STATUS_ACCESS_DENIED") + message(FATAL_ERROR "Permission denied") +endif() + +set(smbscript "${WORKSPACE}/.smbscript") +file(REMOVE "${smbscript}") +file(GLOB files + "${WORKSPACE}/*.tar.gz*" + "${WORKSPACE}/*.zip" + "${WORKSPACE}/*.apk*" + "${WORKSPACE}/*.ipa" + "${WORKSPACE}/*.msi" + "${WORKSPACE}/*.msi.sha*" + "${WORKSPACE}/*.dmg" + "${WORKSPACE}/*.dmg.sha*" + "${WORKSPACE}/*.pkg" + "${WORKSPACE}/*.pkg.sha*" + "${WORKSPACE}/*-ReleaseNotes.pdf" + "${WORKSPACE}/*-NetInstallation_Integration_*.pdf" + "${WORKSPACE}/*-Failure-Codes-*.pdf" + "${WORKSPACE}/*-SDK.pdf" + "${WORKSPACE}/*-Lizenz.*" + "${WORKSPACE}/*_BuildDir.tar.*" +) +foreach(file ${files}) + if(IS_DIRECTORY ${file}) + continue() + endif() + cmake_path(GET file FILENAME filename) + file(RELATIVE_PATH sourcefile "${WORKSPACE}" "${file}") + file(APPEND "${smbscript}" "put ${sourcefile} $ENV{REV}/${filename}\n") +endforeach() + +step(${SMB} INPUT_FILE "${smbscript}") diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Cloud.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Cloud.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Cloud.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Cloud.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,11 @@ +find_program(CURL curl REQUIRED) +list(APPEND CURL --fail -v -u $ENV{CLOUD_USER}:$ENV{CLOUD_PSW}) +set(CLOUD_DESTINATION "$ENV{CLOUD_URL}/$ENV{REV}") + +step(${CURL} -X MKCOL "${CLOUD_DESTINATION}") + +file(GLOB files "${WORKSPACE}/*.msi" "${WORKSPACE}/*.dmg" "${WORKSPACE}/*.pkg" "${WORKSPACE}/*.apk" "${WORKSPACE}/*.ipa") +foreach(file ${files}) + cmake_path(GET file FILENAME filename) + step(${CURL} -T ${file} "${CLOUD_DESTINATION}/${filename}") +endforeach() diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Container.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Container.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Container.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Container.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,10 @@ +include(Utils) + +file(GLOB tarball "${WORKSPACE}/*.tar") +check_beta_version(${tarball}) + +find_program(CMD skopeo REQUIRED) +step(${CMD} login -u $ENV{DOCKERHUB_USER} -p $ENV{DOCKERHUB_PSW} $ENV{DOCKERHUB_URL}) +set(DOCKERHUB docker://$ENV{DOCKERHUB_URL}/$ENV{DOCKERHUB_IMAGE}) +step(${CMD} copy docker-archive:${tarball} ${DOCKERHUB}:$ENV{REV}) +step(${CMD} copy ${DOCKERHUB}:$ENV{REV} ${DOCKERHUB}:latest) diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Framework.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Framework.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Framework.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Framework.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,37 @@ +include(Utils) + +file(GLOB zipfile "${WORKSPACE}/*.zip") +cmake_path(GET zipfile FILENAME zipfilename) +message(STATUS "Use zipfile: ${zipfilename}") +check_beta_version(${zipfile} VERSION version) +message(STATUS "Use zipfile version: ${version}") + +if(NOT DEFINED ENV{GITHUB_REPOSITORY_FRAMEWORK}) + set(ENV{GITHUB_REPOSITORY_FRAMEWORK} "https://$ENV{GH_TOKEN}@github.com/Governikus/AusweisApp2-SDK-iOS.git") +endif() + +set(MAIN master) + +step(git clone --depth 1 $ENV{GITHUB_REPOSITORY_FRAMEWORK} source) +step(${CMAKE_COMMAND} -E make_directory target) +step(${CMAKE_COMMAND} -E tar xf "${zipfile}" CHDIR target) +step(${CMAKE_COMMAND} -E copy_directory source/.git target/.git) + +block() + include(Files) +endblock() +prepare_gpg(GPG_CMD SH) + +step(git -C target config gpg.program ${GPG_CMD}) +step(git -C target config user.name "Governikus") +step(git -C target config user.email "ausweisapp2@governikus.de") +step(git -C target config user.signingkey "2D7479A531451088") + +step(git -C target add .) +step(git -C target commit -S -m "Add revision: v${version}") +step(git -C target tag -s ${version} -m "Add tag ${version}") + +step(git -C target push origin ${MAIN}:${MAIN}) +step(git -C target push origin --tags) + +step(gh release create ${version} --draft --title "${version}" CHDIR target) diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/GitHub.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/GitHub.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/GitHub.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/GitHub.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,70 @@ +include(Utils) + +file(GLOB tarball "${WORKSPACE}/*.tar.gz") +cmake_path(GET tarball FILENAME tarballname) +message(STATUS "Use tarball: ${tarballname}") +check_beta_version(${tarball} VERSION version) +message(STATUS "Use tarball version: ${version}") +string(REGEX MATCH "[0-9]+\\.[0-9]+" MAJOR_MINOR "${version}") +message(STATUS "Use version as branch: ${MAJOR_MINOR}") + +if(NOT DEFINED ENV{GITHUB_REPOSITORY_SOURCE}) + set(ENV{GITHUB_REPOSITORY_SOURCE} "https://$ENV{GH_TOKEN}@github.com/Governikus/AusweisApp.git") +endif() + +set(MAIN master) + +step(git clone --no-checkout $ENV{GITHUB_REPOSITORY_SOURCE} source) +step(git -C source show-ref --verify --quiet refs/remotes/origin/${MAJOR_MINOR} RESULT NO_BRANCH_FOUND) + +if(NO_BRANCH_FOUND) + set(source_branch origin/${MAIN}) +else() + set(source_branch origin/${MAJOR_MINOR}) +endif() + +step(git -C source switch -c ${MAJOR_MINOR} ${source_branch}) +step(${CMAKE_COMMAND} -E make_directory target) +step(tar xf "${tarball}" --strip-components=1 -C target) +step(${CMAKE_COMMAND} -E copy_directory source/.git target/.git) + +block() + include(Files) +endblock() +prepare_gpg(GPG_CMD SH) + +step(git -C target config gpg.program ${GPG_CMD}) +step(git -C target config user.name "Governikus") +step(git -C target config user.email "ausweisapp2@governikus.de") +step(git -C target config user.signingkey "2D7479A531451088") + +step(git -C target add .) +step(git -C target commit -S -m "Add revision: v${version}") +step(git -C target tag -s ${version} -m "Add tag ${version}") + +step(git -C target switch ${MAIN}) +step(git -C target merge ${MAJOR_MINOR} --ff-only) + +step(git -C target switch community) +step(git -C target merge -X theirs ${MAIN} -S -m "Merge v${version} into community") + +step(git -C target push origin ${MAJOR_MINOR}:${MAJOR_MINOR}) +step(git -C target push origin ${MAIN}:${MAIN}) +step(git -C target push origin community:community) +step(git -C target push origin --tags) + +step(gh release create ${version} --draft --title "${version}" --notes "# Anwender\n* TODO\n\n\n# Entwickler\n* TODO" CHDIR target) +file(GLOB files + "${WORKSPACE}/*.tar.gz*" + "${WORKSPACE}/*.apk" + "${WORKSPACE}/*.apk.sha*" + "${WORKSPACE}/*.msi" + "${WORKSPACE}/*.msi.sha*" + "${WORKSPACE}/*.dmg" + "${WORKSPACE}/*.dmg.sha*" + "${WORKSPACE}/*-ReleaseNotes.pdf" + "${WORKSPACE}/*-NetInstallation_Integration.pdf" + "${WORKSPACE}/*-SDK.pdf" + "${WORKSPACE}/*-Lizenz.txt" +) +step(gh release upload ${version} ${files} CHDIR target) diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/IPA.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/IPA.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/IPA.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/IPA.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,7 @@ +if(DEFINED ENV{JENKINS_HOME}) + file(GLOB ipafile "${T_BUILD_DIR}/*.ipa") +else() + file(GLOB ipafile "${WORKSPACE}/*.ipa") +endif() +step(xcrun altool -t ios --validate-app --verbose -u $ENV{APPSTORE_USER} -p @env:APPSTORE_PSW -f ${ipafile}) +step(xcrun altool -t ios --upload-app -u $ENV{APPSTORE_USER} -p @env:APPSTORE_PSW -f ${ipafile}) diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/PKG.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/PKG.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/PKG.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/PKG.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,6 @@ +if(DEFINED ENV{JENKINS_HOME}) + file(GLOB pkgfile "${T_BUILD_DIR}/*.pkg") +else() + file(GLOB pkgfile "${WORKSPACE}/*.pkg") +endif() +step(xcrun altool -t osx --upload-app -u $ENV{APPSTORE_USER} -p @env:APPSTORE_PSW -f ${pkgfile}) diff -Nru ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Utils.cmake ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Utils.cmake --- ausweisapp2-2.3.1/ci/cmake/Deploy.cmake/Utils.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Deploy.cmake/Utils.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,42 @@ +function(check_beta_version _files) + set(oneValueArgs RESULT VERSION) + cmake_parse_arguments(_PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(DEFINED _PARAM_RESULT) + set(msgLevel STATUS) + else() + set(msgLevel FATAL_ERROR) + endif() + + foreach(file IN LISTS _files) + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+\\+?" VERSION "${file}") + if(NOT VERSION) + message(FATAL_ERROR "Cannot extract version of file: ${file}") + endif() + + if(DEFINED _PARAM_VERSION) + string(REPLACE "+" "" _out "${VERSION}") + set(${_PARAM_VERSION} ${_out} PARENT_SCOPE) + endif() + + message(STATUS "Check file for beta/snapshot: ${file}") + if(VERSION MATCHES "\\+") + message(${msgLevel} "Version seems to be a snapshot: ${VERSION}") + if(DEFINED _PARAM_RESULT) + set(${_PARAM_RESULT} ON PARENT_SCOPE) + endif() + return() + endif() + + string(REGEX MATCHALL "[0-9]+" VERSION_PARTS "${VERSION}") + foreach(NUM ${VERSION_PARTS}) + if(NUM GREATER_EQUAL 100) + message(${msgLevel} "Version seems to be a beta release: ${VERSION}") + if(DEFINED _PARAM_RESULT) + set(${_PARAM_RESULT} ON PARENT_SCOPE) + endif() + return() + endif() + endforeach() + endforeach() +endfunction() diff -Nru ausweisapp2-2.3.1/ci/cmake/Docker.cmake ausweisapp2-2.4.0/ci/cmake/Docker.cmake --- ausweisapp2-2.3.1/ci/cmake/Docker.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Docker.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1 @@ +include(Container) diff -Nru ausweisapp2-2.3.1/ci/cmake/Docs.cmake ausweisapp2-2.4.0/ci/cmake/Docs.cmake --- ausweisapp2-2.3.1/ci/cmake/Docs.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Docs.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,22 @@ +step(${T_CFG} --preset ci-tools) + +step(${T_TARGET} notes) +step(${T_TARGET} notes.latex.pdf) +step(${CMAKE_COMMAND} -E tar cfJ ../AusweisApp_ReleaseNotes.tar.xz . CHDIR ${T_BUILD_DIR}/docs/notes) + +step(${T_TARGET} sdk) +step(${T_TARGET} sdk.latex.pdf) +step(${CMAKE_COMMAND} -E tar cfJ ../AusweisApp_SDK.tar.xz . CHDIR ${T_BUILD_DIR}/docs/sdk/html) + +step(${T_TARGET} failurecodes) +step(${T_TARGET} failurecodes_en.latex.pdf) +step(${T_TARGET} failurecodes_de.latex.pdf) + +step(${T_TARGET} installation_integration_de.latex.pdf) +step(${T_TARGET} installation_integration_en.latex.pdf) + +step(${T_TARGET} license) + +if(NOT RELEASE) + step(${T_TARGET} doc8) +endif() diff -Nru ausweisapp2-2.3.1/ci/cmake/Files.cmake ausweisapp2-2.4.0/ci/cmake/Files.cmake --- ausweisapp2-2.3.1/ci/cmake/Files.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Files.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,162 @@ +if(NOT DEFINED ENV{JENKINS_HOME}) + if(NOT DEFINED ENV{CI_API_V4_URL}) + set(ENV{CI_API_V4_URL} "https://gitlab.govkg.de/api/v4") + endif() + if(NOT DEFINED ENV{CI_PROJECT_ID}) + set(ENV{CI_PROJECT_ID} 786) + endif() + + if(DEFINED ENV{CI_JOB_TOKEN}) + set(ENV{TOKEN_TYPE} "JOB-TOKEN") + set(ENV{TOKEN} $ENV{CI_JOB_TOKEN}) + elseif(DEFINED ENV{CI_PIPELINE_TOKEN}) + set(ENV{TOKEN_TYPE} "PRIVATE-TOKEN") + set(ENV{TOKEN} $ENV{CI_PIPELINE_TOKEN}) + else() + message(FATAL_ERROR "No CI_JOB_TOKEN / CI_PIPELINE_TOKEN found") + endif() +endif() + +function(handle_http _file) + set(options UPLOAD ALLOW_404) + set(multiValueArgs URLS HEADER) + cmake_parse_arguments(_PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(_PARAM_HEADER) + foreach(_header ${_PARAM_HEADER}) + list(APPEND HEADER HTTPHEADER "${_header}") + endforeach() + else() + list(APPEND HEADER HTTPHEADER "$ENV{TOKEN_TYPE}: $ENV{TOKEN}") + endif() + + foreach(url ${_PARAM_URLS}) + if(_PARAM_UPLOAD) + set(operation "Upload") + message(STATUS "Upload file: ${url}") + if(NOT ECHO) + if(NOT DEFINED ENV{CI}) + message(FATAL_ERROR "No CI detected: skip upload!") + return() + endif() + file(UPLOAD "${_file}" "${url}" ${HEADER} SHOW_PROGRESS STATUS status LOG output) + endif() + else() + set(operation "Download") + message(STATUS "Download file: ${url}") + if(NOT ECHO) + file(DOWNLOAD "${url}" "${_file}" ${HEADER} SHOW_PROGRESS STATUS status LOG output) + endif() + endif() + + list(GET status 0 status_code) + if(status_code EQUAL 0) + message(STATUS "${operation} succeeded: ${_file}") + return() + else() + message(STATUS "${operation} failed: ${_file}") + if(NOT _PARAM_UPLOAD) + step(${CMAKE_COMMAND} -E rm ${_file}) + endif() + + if(NOT ECHO) + string(REGEX REPLACE "\\[[0-9]+ bytes data\\]" "" output "${output}") + string(REGEX REPLACE "$ENV{TOKEN}" "***" output "${output}") + string(REGEX MATCH "HTTP/[0-9.]+ 404" http_404 "${output}") + if(http_404 AND _PARAM_ALLOW_404) + continue() + endif() + message(FATAL_ERROR ${output}) + endif() + endif() + endforeach() +endfunction() + +function(handle_pkg _file _dirs _version) + foreach(dir ${_dirs}) + cmake_path(GET _file FILENAME pkg) + list(APPEND URLS "$ENV{CI_API_V4_URL}/projects/$ENV{CI_PROJECT_ID}/packages/generic/${dir}/${_version}/${pkg}") + endforeach() + + handle_http("${_file}" ${ARGN} ALLOW_404 URLS ${URLS}) +endfunction() + +function(handle_pkg_libs _file _dirs) + foreach(dir IN LISTS _dirs) + list(APPEND suffixedDirs "${dir}-OpenSSL_${LIBS_OPENSSL}-Qt_${LIBS_QT}") + endforeach() + handle_pkg("${_file}" "${suffixedDirs}" "${LIBS_IDENTIFIER}" ${ARGN}) +endfunction() + +function(download_pkg_libs _file) + list(APPEND dir ${LIBS_BRANCH}) + if(REVIEW AND LIBS_PHASE) + list(APPEND dir ${LIBS_BRANCH}-${LIBS_PHASE}) + endif() + handle_pkg_libs("${_file}" "${dir}") +endfunction() + +function(upload_pkg_libs _file) + set(dir ${LIBS_BRANCH}) + if(LIBS_PHASE) + set(dir ${dir}-${LIBS_PHASE}) + endif() + handle_pkg_libs("${_file}" "${dir}" UPLOAD) +endfunction() + +function(download_file _file _id) + list(APPEND URLS "$ENV{CI_API_V4_URL}/projects/$ENV{CI_PROJECT_ID}/secure_files/${_id}/download") + handle_http("${_file}" URLS ${URLS}) +endfunction() + +function(prepare_android_keystore _store _prefix) + if(NOT DEFINED ENV{GITLAB_CI}) + return() + endif() + + string(TOLOWER "${_store}" filename) + set(store "$ENV{HOME}/.android/${filename}.keystore") + set(prefixEnv "ANDROID_KEYSTORE_${_store}") + set(file_id "${prefixEnv}_FILE_ID") + set(alias "${prefixEnv}_ALIAS") + set(psw "${prefixEnv}_PSW") + + if(NOT EXISTS "${store}") + if(NOT DEFINED ENV{${file_id}}) + message(FATAL_ERROR "Missing variable: ${file_id}") + endif() + download_file("${store}" $ENV{${file_id}}) + if(NOT EXISTS "${store}") + message(FATAL_ERROR "Cannot fetch file: ${store}") + endif() + endif() + + set("${_prefix}" "${store}" PARENT_SCOPE) + set("${_prefix}_ALIAS" "$ENV{${alias}}" PARENT_SCOPE) + set("${_prefix}_PSW" "$ENV{${psw}}" PARENT_SCOPE) +endfunction() + +function(prepare_gpg _var) + set(options SH) + cmake_parse_arguments(_PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(GPG_CMD gpg --batch --passphrase $ENV{GPG_PSW} --pinentry-mode loopback) + if(_PARAM_SH) + list(JOIN GPG_CMD " " GPG_CMD) + string(REPLACE "$ENV" "\$" GPG_CMD "${GPG_CMD}") + set(GPG_CMD_SH "${WORKSPACE}/.gpg") + file(WRITE "${GPG_CMD_SH}" "#!/bin/sh\n") + file(APPEND "${GPG_CMD_SH}" "set -euo pipefail\n") + file(APPEND "${GPG_CMD_SH}" "${GPG_CMD} $@\n") + file(CHMOD "${GPG_CMD_SH}" FILE_PERMISSIONS OWNER_READ OWNER_EXECUTE) + set(GPG_CMD "${GPG_CMD_SH}") + endif() + + if(DEFINED ENV{GITLAB_CI} AND NOT EXISTS "$ENV{HOME}/.gnupg") + set(gnupg_private "${WORKSPACE}/gnupg-private.asc") + download_file("${gnupg_private}" $ENV{GPG_FILE_ID}) + step(${GPG_CMD} --import ${gnupg_private}) + endif() + + set(${_var} ${GPG_CMD} PARENT_SCOPE) +endfunction() diff -Nru ausweisapp2-2.3.1/ci/cmake/Formatting.cmake ausweisapp2-2.4.0/ci/cmake/Formatting.cmake --- ausweisapp2-2.3.1/ci/cmake/Formatting.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Formatting.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,20 @@ +if(RELEASE) + return() +endif() + +block() + include(Libraries) +endblock() + +set(CHECK_SH ${CMAKE_SOURCE_DIR}/ci/check_formatting.sh) + +step(${T_CFG} --preset ci-formatting) +if(REVIEW) + IMPORT_PATCH(_unused --clean-dir-only) + step(${CHECK_SH} ${PENDING_PATCH} CHDIR ${CMAKE_SOURCE_DIR}) +endif() + +# If script was modified we need to re-check with modified script again. +step(${CHECK_SH} CHDIR ${CMAKE_SOURCE_DIR} ENV NO_PATCH=ON) + +step(${CMAKE_COMMAND} -DCMD=CHECK_FAILURE_CODES -P ${CMAKE_DIR}/cmd.cmake CHDIR ${CMAKE_SOURCE_DIR}) diff -Nru ausweisapp2-2.3.1/ci/cmake/FreeBSD.cmake ausweisapp2-2.4.0/ci/cmake/FreeBSD.cmake --- ausweisapp2-2.3.1/ci/cmake/FreeBSD.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/FreeBSD.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,9 @@ +block() + include(Libraries) +endblock() + +set(LD_ENV LD_LIBRARY_PATH=${WORKSPACE}/libs/dist/lib) + +step(${T_CFG} --preset ci-bsd) +step(${T_BUILD} ENV ${LD_ENV}) +step(${T_CTEST} ENV ${LD_ENV} QT_PLUGIN_PATH=${WORKSPACE}/libs/dist/plugins QML2_IMPORT_PATH=${WORKSPACE}/libs/dist/qml) diff -Nru ausweisapp2-2.3.1/ci/cmake/Libraries.cmake ausweisapp2-2.4.0/ci/cmake/Libraries.cmake --- ausweisapp2-2.3.1/ci/cmake/Libraries.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Libraries.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,118 @@ +if("Vanilla" IN_LIST NAMES) + message(STATUS "Use libraries: System") + return() +elseif("Qt" IN_LIST NAMES) + message(STATUS "Use libraries: Qt") + find_program(AQT aqt REQUIRED) +else() + message(STATUS "Use libraries: Governikus") + if(DEFINED ENV{JENKINS_HOME}) + message(STATUS "Use libraries: Jenkins") + return() + endif() +endif() + +if(EXISTS ${T_LIBS_DIR}) + step(${CMAKE_COMMAND} -E rm -r ${T_LIBS_DIR}) +endif() + +if(AQT) + if(NOT QT) + message(FATAL_ERROR "Provide version via -DQT=") + endif() + step(${AQT} install-qt -O ${T_LIBS_DIR} linux desktop ${QT} --modules qtwebsockets qtscxml qtconnectivity) + step(${CMAKE_COMMAND} -E create_symlink ${QT}/gcc_64 ${T_LIBS_DIR}/dist) + return() +endif() + + + +if("FreeBSD" IN_LIST NAMES) + set(PRESET ci-debug) +elseif("Android" IN_LIST NAMES) + set(PRESET ci-android) +elseif("iOS" IN_LIST NAMES) + if("Simulator" IN_LIST NAMES) + set(PRESET ci-ios-simulator) + if("x86" IN_LIST NAMES AND "64" IN_LIST NAMES) + set(PRESET ${PRESET}-x86_64) + elseif("arm64" IN_LIST NAMES) + set(PRESET ${PRESET}-arm64) + endif() + else() + set(PRESET ci-ios) + endif() + + step(security unlock-keychain $ENV{KEYCHAIN_CREDENTIALS} $ENV{HOME}/Library/Keychains/login.keychain-db) +elseif("Win" IN_LIST NAMES) + if("GNU" IN_LIST NAMES) + set(PRESET ci-gnu-release) + else() + set(VCVARS cmd /c vcvarsall.bat amd64 && call) + if("dev" IN_LIST NAMES) + set(PRESET ci-msvc-debug) + else() + set(PRESET ci-msvc-release) + endif() + endif() +else() + set(PRESET ci-release) +endif() + +CALC_CHECKSUM(SALT ${CMAKE_CURRENT_LIST_FILE}) +if(CMAKE_PARENT_LIST_FILE STREQUAL CMAKE_SCRIPT_MODE_FILE) + set(LIBRARIES_SCRIPT_ONLY ON) +endif() + +if(LIBRARIES_SCRIPT_ONLY OR PROPAGATE) + set(cfg ${T_CFG_LIBS}) +else() + set(cfg ${T_CFG_LIBS_DEPS}) +endif() + +if(DEFINED OPENSSL) + list(APPEND CUSTOM_VERSION -DOPENSSL=${OPENSSL}) +endif() +if(DEFINED QT) + list(APPEND CUSTOM_VERSION -DQT=${QT}) +endif() + +step(${VCVARS} ${cfg} --preset ${PRESET} -DSALT=${SALT} ${CUSTOM_VERSION} NO_ECHO) +find_package(Governikus REQUIRED PATHS ${T_LIBS_DIR} NO_CMAKE_PATH NO_CMAKE_SYSTEM_PATH) +set(CACHED_TARBALL ${CACHE_DIR}/${LIBS_TARBALL}) + +block() + include(Files) +endblock() + +if(EXISTS "${CACHED_TARBALL}") + message(STATUS "Use cached libraries: ${LIBS_TARBALL}") +elseif(NOT PROVIDER_NO_DOWNLOAD) + download_pkg_libs("${CACHED_TARBALL}") +endif() + +if(NOT EXISTS "${CACHED_TARBALL}" AND NOT PROVIDER_NO_BUILD) + message(STATUS "Build libraries...") + if("Win" IN_LIST NAMES) + if("GNU" IN_LIST NAMES) + if(NOT $ENV{GIT_STRATEGY} STREQUAL "none") + set(PATH_SUFFIX -$ENV{GIT_STRATEGY}) + endif() + step(C:/msys64/usr/bin/bash.exe --login -c "cmake -E chdir /gitlab/$ENV{CI_CONCURRENT_ID}/$ENV{CI_PROJECT_PATH}${PATH_SUFFIX} cmake --build ${T_LIBS_DIR} --target openssl" ENV MSYS2_PATH_TYPE=inherit) + endif() + set(OPTIONAL_PATH PATH ${WORKSPACE}/${T_LIBS_DIR}/dist/bin) + endif() + + step(${VCVARS} ${CMAKE_COMMAND} --build ${T_LIBS_DIR} --target compress ${OPTIONAL_PATH}) + step(${CMAKE_COMMAND} -E copy_if_different "${T_LIBS_DIR}/${LIBS_TARBALL}" "${CACHED_TARBALL}") + if(DEFINED ENV{GITLAB_CI}) + upload_pkg_libs("${CACHED_TARBALL}") + endif() +endif() + +step(${CMAKE_COMMAND} -E rm -r ${T_LIBS_DIR}) + +if(NOT LIBRARIES_SCRIPT_ONLY) + step(${CMAKE_COMMAND} -E make_directory ${T_LIBS_DIR}) + step(${CMAKE_COMMAND} -E tar xf "${CACHED_TARBALL}" CHDIR ${T_LIBS_DIR}) +endif() diff -Nru ausweisapp2-2.3.1/ci/cmake/Light.cmake/Analyze.cmake ausweisapp2-2.4.0/ci/cmake/Light.cmake/Analyze.cmake --- ausweisapp2-2.3.1/ci/cmake/Light.cmake/Analyze.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Light.cmake/Analyze.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1 @@ +include(SonarQube) diff -Nru ausweisapp2-2.3.1/ci/cmake/Light.cmake/Configuration.cmake ausweisapp2-2.4.0/ci/cmake/Light.cmake/Configuration.cmake --- ausweisapp2-2.3.1/ci/cmake/Light.cmake/Configuration.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Light.cmake/Configuration.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,8 @@ +block() + include(Libraries) +endblock() + +step(${T_CFG} --preset ci-linux) +step(${T_TARGET} ALL_Test_configuration) +step(${T_CTEST} -R Test_configuration ENV QT_PLUGIN_PATH=${WORKSPACE}/libs/dist/plugins) +step(${T_CTEST} -L json) diff -Nru ausweisapp2-2.3.1/ci/cmake/Light.cmake/Packages.cmake ausweisapp2-2.4.0/ci/cmake/Light.cmake/Packages.cmake --- ausweisapp2-2.3.1/ci/cmake/Light.cmake/Packages.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Light.cmake/Packages.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1 @@ +step(${CMAKE_SOURCE_DIR}/ci/pipeline.py --packages delete) diff -Nru ausweisapp2-2.3.1/ci/cmake/Linux.cmake ausweisapp2-2.4.0/ci/cmake/Linux.cmake --- ausweisapp2-2.3.1/ci/cmake/Linux.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Linux.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,28 @@ +block(PROPAGATE LIBS_GOVERNIKUS) + include(Libraries) +endblock() + +if("Integrated" IN_LIST NAMES) + set(INTEGRATED ON) +endif() + +if(INTEGRATED) + set(PRESET ci-integrated) +else() + set(PRESET ci-linux) +endif() + +step(${T_CFG} --preset ${PRESET}) +step(${T_BUILD}) +step(${T_CTEST} ENV QT_PLUGIN_PATH=${WORKSPACE}/libs/dist/plugins QML2_IMPORT_PATH=${WORKSPACE}/libs/dist/qml) +step(${CMAKE_COMMAND} --install ${T_BUILD_DIR} ENV DESTDIR=${WORKSPACE}/install) + +if(LIBS_GOVERNIKUS) + step(${T_TARGET} gcovr) +endif() + +if(DAILY) + if(DEFINED ENV{JENKINS_HOME}) + step(${T_TARGET} cloc.report) + endif() +endif() diff -Nru ausweisapp2-2.3.1/ci/cmake/MacOS.cmake ausweisapp2-2.4.0/ci/cmake/MacOS.cmake --- ausweisapp2-2.3.1/ci/cmake/MacOS.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/MacOS.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,59 @@ +block() + include(Libraries) +endblock() + +if("Integrated" IN_LIST NAMES) + set(INTEGRATED ON) +endif() + +if("DMG" IN_LIST NAMES OR "PKG" IN_LIST NAMES OR RELEASE) + set(PACKAGES ON) +endif() + +if(PACKAGES) + set(PRESET ci-macos-release) +elseif(INTEGRATED) + set(PRESET ci-macos-integrated) +else() + set(PRESET ci-macos) + set(CTEST_CFG -C Debug) +endif() + +step(security unlock-keychain $ENV{KEYCHAIN_CREDENTIALS} $ENV{HOME}/Library/Keychains/login.keychain-db) + +step(${T_CFG} --preset ${PRESET}) + +if(PACKAGES) + step(${T_TARGET} package --config MinSizeRel) + step(${CMAKE_COMMAND} -E tar cf ../../AusweisApp.app.dSYM.zip --format=zip AusweisApp.app.dSYM CHDIR ${T_BUILD_DIR}/src/MinSizeRel) + + file(GLOB_RECURSE apps LIST_DIRECTORIES ON "${T_BUILD_DIR}/_CPack_Packages/Darwin") + list(FILTER apps INCLUDE REGEX "\\.app$") + set(dragndrop ${apps}) + list(FILTER dragndrop INCLUDE REGEX "/DragNDrop/") + if(NOT dragndrop OR NOT apps) + message(FATAL_ERROR "no *.app directory found") + endif() + foreach(app ${apps}) + step(codesign -vvvv ${app}) + endforeach() + foreach(app ${dragndrop}) + step(spctl -a -vv ${app}) + endforeach() + + if(NOT REVIEW) + step(${CMAKE_COMMAND} -P ${CMAKE_DIR}/Notarization.cmake CHDIR ${T_BUILD_DIR}) + endif() + + file(GLOB FILES "${T_BUILD_DIR}/*.dmg") + hashsum(${FILES}) +else() + step(${T_BUILD}) + step(${T_CTEST} ${CTEST_CFG} ENV QT_PLUGIN_PATH=${WORKSPACE}/libs/dist/plugins QML2_IMPORT_PATH=${WORKSPACE}/libs/dist/qml) +endif() + +if(DEFINED ENV{JENKINS_HOME} AND PACKAGES AND RELEASE AND DEFINED ENV{USE_DISTRIBUTION_PROFILE}) + if($ENV{USE_DISTRIBUTION_PROFILE}) + include(${CMAKE_CI_DIR}/Deploy.cmake/PKG.cmake) + endif() +endif() diff -Nru ausweisapp2-2.3.1/ci/cmake/Provider.cmake ausweisapp2-2.4.0/ci/cmake/Provider.cmake --- ausweisapp2-2.3.1/ci/cmake/Provider.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Provider.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,11 @@ +if(PROVIDER MATCHES "(bld|build)") + set(PROVIDER_NO_DOWNLOAD ON) +elseif(PROVIDER MATCHES "(dl|download)") + set(PROVIDER_NO_BUILD ON) +endif() + +include(Libraries) + +if(EXISTS "${T_LIBS_DIR}") + step(${CMAKE_COMMAND} -E rename "${T_LIBS_DIR}/dist" "${CMAKE_BINARY_DIR}/provider") +endif() diff -Nru ausweisapp2-2.3.1/ci/cmake/SonarQube.cmake ausweisapp2-2.4.0/ci/cmake/SonarQube.cmake --- ausweisapp2-2.3.1/ci/cmake/SonarQube.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/SonarQube.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,133 @@ +block() + include(Libraries) +endblock() + +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}") + +set(SONARSCANNERCLI_VERSION 7.3.0.5189) # https://binaries.sonarsource.com/?prefix=Distribution/sonar-scanner-cli/ +set(SONARSCANNERCLI_ZIP_NAME sonar-scanner-cli-${SONARSCANNERCLI_VERSION}.zip) +set(SONARSCANNERCLI_URL https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/${SONARSCANNERCLI_ZIP_NAME}) +set(SONARSCANNERCLI_HASH a251d0793cb6bd889e4fd30299bb5dc4e07433e57133b16fc227aca98f8d2c2d) + +set(DEPENDENCYCHECK_VERSION 12.1.8) # https://github.com/dependency-check/DependencyCheck +set(DEPENDENCYCHECK_ZIP_NAME dependency-check-${DEPENDENCYCHECK_VERSION}-release.zip) +set(DEPENDENCYCHECK_URL https://github.com/dependency-check/DependencyCheck/releases/download/v${DEPENDENCYCHECK_VERSION}/${DEPENDENCYCHECK_ZIP_NAME}) +set(DEPENDENCYCHECK_HASH bd51cf867950ffcfc4e074c43948127f450fceedc93f628d52815588412d38a6) + +set(MARIADB_CONNECTOR_VERSION 3.5.6) +set(MARIADB_CONNECTOR_ZIP_NAME mariadb-java-client-${MARIADB_CONNECTOR_VERSION}.jar) +set(MARIADB_CONNECTOR_URL https://downloads.mariadb.com/Connectors/java/connector-java-${MARIADB_CONNECTOR_VERSION}/${MARIADB_CONNECTOR_ZIP_NAME}) +set(MARIADB_CONNECTOR_HASH a129703efd7b0f334564d46753de999f09b3a361489a2eb647e6020390981cc9) + +set(SONARQUBETOOLS_DIR ${WORKSPACE}/sonarqubetools) +step(${CMAKE_COMMAND} -E make_directory ${SONARQUBETOOLS_DIR}) + +function(DOWNLOAD_AND_EXTRACT NAME URL ZIP_NAME HASH EXTRACT_DIR RESULT_DIR EXTRACT) + if(EXISTS "${SONARQUBETOOLS_DIR}/${RESULT_DIR}" AND EXTRACT) + message(STATUS "Removing previous ${NAME}") + step(${CMAKE_COMMAND} -E rm -rf ${SONARQUBETOOLS_DIR}/${RESULT_DIR}) + endif() + + if(NOT EXISTS "${PACKAGES_DIR}/${ZIP_NAME}" OR NOT HASH) + message(STATUS "Download ${NAME}: ${ZIP_NAME}") + file(DOWNLOAD ${URL} ${PACKAGES_DIR}/${ZIP_NAME}) + endif() + + if(HASH) + file(SHA256 ${PACKAGES_DIR}/${ZIP_NAME} FILE_HASH) + if(NOT "${FILE_HASH}" STREQUAL "${HASH}") + message(FATAL_ERROR "${NAME} hash does not match! Current: ${FILE_HASH}, expected: ${HASH}") + endif() + endif() + + message(STATUS "Install ${NAME}: ${ZIP_NAME}") + if(EXTRACT) + step(${CMAKE_COMMAND} -E tar xf ${PACKAGES_DIR}/${ZIP_NAME} CHDIR ${SONARQUBETOOLS_DIR}) + if(NOT "${EXTRACT_DIR}" STREQUAL "${RESULT_DIR}") + step(${CMAKE_COMMAND} -E rename ${SONARQUBETOOLS_DIR}/${EXTRACT_DIR} ${SONARQUBETOOLS_DIR}/${RESULT_DIR}) + endif() + else() + step(${CMAKE_COMMAND} -E copy_if_different ${PACKAGES_DIR}/${MARIADB_CONNECTOR_ZIP_NAME} ${SONARQUBETOOLS_DIR}/${RESULT_DIR}) + endif() +endfunction() + +DOWNLOAD_AND_EXTRACT("SonarScanner" ${SONARSCANNERCLI_URL} ${SONARSCANNERCLI_ZIP_NAME} ${SONARSCANNERCLI_HASH} "sonar-scanner-${SONARSCANNERCLI_VERSION}" "sonar-scanner" TRUE) +DOWNLOAD_AND_EXTRACT("Dependency Check" ${DEPENDENCYCHECK_URL} ${DEPENDENCYCHECK_ZIP_NAME} ${DEPENDENCYCHECK_HASH} "dependency-check" "dependency-check" TRUE) +DOWNLOAD_AND_EXTRACT("Dependency Check MariaDB Connector" ${MARIADB_CONNECTOR_URL} ${MARIADB_CONNECTOR_ZIP_NAME} ${MARIADB_CONNECTOR_HASH} "dependency-check/lib/" "dependency-check/lib/" FALSE) + +set(SONAR_DIR "${WORKSPACE}/sonar") +if(NOT EXISTS ${SONAR_DIR}) + step(${CMAKE_COMMAND} -E make_directory ${SONAR_DIR}) +endif() + +step(${T_CFG} --preset ci-linux) + +step(${SONARQUBETOOLS_DIR}/dependency-check/bin/dependency-check.sh + --enableExperimental -f HTML -f JSON --scan ${CMAKE_DIR} --noupdate + --connectionString=jdbc:mariadb://dependency-check-db.govkg.de/dependencycheck + --dbUser=$ENV{DEPENDENCY_CHECK_USER} + --dbPassword=$ENV{DEPENDENCY_CHECK_PASSWORD} + --dbDriverName=org.mariadb.jdbc.Driver + CHDIR ${T_BUILD_DIR} +) + +step(${T_BUILD}) + +step(${T_CTEST} -LE qml -E Test_ui_qml_Qml) + +step(${T_TARGET} gcovr.sonar) + +if(DEFINED ENV{GITLAB_CI}) + set(BRANCH $ENV{REV}) +elseif(DEFINED ENV{JENKINS_HOME}) + set(BRANCH $ENV{MERCURIAL_REVISION_BRANCH}) +else() + message(FATAL_ERROR "Unknown branch for SonarQube") +endif() + +if(REVIEW) + if(DEFINED ENV{REVIEWBOARD_REVIEW_ID}) + set(KEY $ENV{REVIEWBOARD_REVIEW_ID}) + elseif(DEFINED ENV{GITLAB_CI}) + set(KEY pipeline-$ENV{CI_PIPELINE_ID}) + elseif(DEFINED ENV{JENKINS_HOME}) + set(KEY $ENV{BUILD_TAG}) + else() + message(FATAL_ERROR "Unknown key for SonarQube") + endif() + + set(SONAR_CMDLINE + -Dsonar.pullrequest.key=${KEY} + -Dsonar.pullrequest.branch=${BRANCH} + -Dsonar.pullrequest.base=${BRANCH} + ) +else() + set(SONAR_CMDLINE + -Dsonar.branch.name=${BRANCH} + ) +endif() + +if(DEFINED ENV{JENKINS_HOME}) + list(APPEND SONAR_CMDLINE -Dsonar.cfamily.threads=4) +elseif(DEFINED ENV{CMAKE_BUILD_PARALLEL_LEVEL}) + list(APPEND SONAR_CMDLINE -Dsonar.cfamily.threads=$ENV{CMAKE_BUILD_PARALLEL_LEVEL}) +endif() + +step( + ${SONARQUBETOOLS_DIR}/sonar-scanner/bin/sonar-scanner + -Dsonar.scanner.metadataFilePath=${T_TEMP}/sonar-metadata.txt + ${SONAR_CMDLINE} + -Dsonar.qualitygate.wait=true + -Dsonar.qualitygate.timeout=90 + -Dsonar.cfamily.analysisCache.mode=fs + -Dsonar.cfamily.analysisCache.path=${SONAR_DIR} + -Dsonar.scanner.skipJreProvisioning=true + CHDIR ${T_BUILD_DIR} + ENV SONAR_USER_HOME=${SONAR_DIR}/home +) diff -Nru ausweisapp2-2.3.1/ci/cmake/Source.cmake ausweisapp2-2.4.0/ci/cmake/Source.cmake --- ausweisapp2-2.3.1/ci/cmake/Source.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Source.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,11 @@ +step(${T_CFG} --preset ci-tools) +step(${T_TARGET} package_source) + +file(GLOB TARBALL "${T_BUILD_DIR}/*.tar.gz") +hashsum(${TARBALL}) + +block() + include(Files) +endblock() +prepare_gpg(GPG_CMD) +step(${GPG_CMD} -a --detach-sig -u $ENV{GPG_ID} ${TARBALL}) diff -Nru ausweisapp2-2.3.1/ci/cmake/Translations.cmake ausweisapp2-2.4.0/ci/cmake/Translations.cmake --- ausweisapp2-2.3.1/ci/cmake/Translations.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Translations.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,6 @@ +block() + include(Libraries) +endblock() + +step(${T_CFG} --preset ci-translations) +step(${T_BUILD} --target update.translations) diff -Nru ausweisapp2-2.3.1/ci/cmake/Win.cmake ausweisapp2-2.4.0/ci/cmake/Win.cmake --- ausweisapp2-2.3.1/ci/cmake/Win.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Win.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,51 @@ +block() + include(Libraries) +endblock() + +if("MSI" IN_LIST NAMES) + set(PACKAGES ON) +endif() + +if("GNU" IN_LIST NAMES) + set(GNU ON) +elseif(("clang" IN_LIST NAMES OR "clang-cl" IN_LIST NAMES) AND DEFINED ENV{LLVM}) + set(PATH "$ENV{LLVM}") +endif() + +if(NOT GNU) + set(VCVARS cmd /c vcvarsall.bat amd64 && call) +endif() + + +if(PACKAGES) + if("dev" IN_LIST NAMES) + set(PRESET ci-win-debug) + else() + set(PRESET ci-win-release) + endif() +else() + set(PRESET ci-win) +endif() + +step(${VCVARS} ${T_CFG} --preset ${PRESET} PATH "${PATH}") + +if(PACKAGES) + step(${VCVARS} ${T_TARGET} package) + step(${VCVARS} ${T_TARGET} package.sign) + file(GLOB FILES "${T_BUILD_DIR}/*.msi") + hashsum(${FILES}) + step(${CMAKE_COMMAND} -DCMD=GENERATE_APPCAST -P ${CMAKE_DIR}/cmd.cmake CHDIR ${T_BUILD_DIR}) + if(NOT RELEASE) + step(${CMAKE_COMMAND} -DCMD=CHECK_WIX_MSI -DFILE=${T_BUILD_DIR}/_CPack_Packages/win64/WIX/wix.log -P ${CMAKE_DIR}/cmd.cmake) + + set(VALIDATION_FILE ${T_BUILD_DIR}/_CPack_Packages/win64/WIX/validation.log) + step(wix msi validate ${FILES} OUTPUT_FILE ${VALIDATION_FILE} RESULT _ignore) + step(${CMAKE_COMMAND} -DCMD=CHECK_WIX_VALIDATION -DFILE=${VALIDATION_FILE} -P ${CMAKE_DIR}/cmd.cmake) + endif() +else() + step(${VCVARS} ${T_BUILD}) +endif() + +if(NOT RELEASE AND NOT PACKAGES) + step(${T_CTEST} ENV QT_PLUGIN_PATH=${WORKSPACE}/libs/dist/plugins QML2_IMPORT_PATH=${WORKSPACE}/libs/dist/qml PATH "${WORKSPACE}/libs/dist/bin") +endif() diff -Nru ausweisapp2-2.3.1/ci/cmake/Win64.cmake ausweisapp2-2.4.0/ci/cmake/Win64.cmake --- ausweisapp2-2.3.1/ci/cmake/Win64.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/Win64.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1 @@ +include(Win) diff -Nru ausweisapp2-2.3.1/ci/cmake/iOS.cmake ausweisapp2-2.4.0/ci/cmake/iOS.cmake --- ausweisapp2-2.3.1/ci/cmake/iOS.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/cmake/iOS.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,56 @@ +if("IPA" IN_LIST NAMES) + set(IPA ON) + set(TARGET ipa) + set(PRESET ci-ios) +elseif("Framework" IN_LIST NAMES) + set(TARGET zip) + set(PRESET ci-ios-framework) + if("Simulator" IN_LIST NAMES) + set(PRESET ${PRESET}-simulator) + if("x86" IN_LIST NAMES AND "64" IN_LIST NAMES) + set(PRESET ${PRESET}-x86_64) + elseif("arm64" IN_LIST NAMES) + set(PRESET ${PRESET}-arm64) + endif() + endif() +elseif("SwiftPackage" IN_LIST NAMES) + step(${CMAKE_COMMAND} -DDIST_DIR=${T_DIST_DIR} -P ${CMAKE_DIR}/SwiftPackage.cmake) + return() +endif() + +block() + include(Libraries) +endblock() + +if("Simulator" IN_LIST NAMES) + set(PLATFORM "iOS Simulator") +else() + set(PLATFORM "iOS") +endif() + +step(security unlock-keychain $ENV{KEYCHAIN_CREDENTIALS} $ENV{HOME}/Library/Keychains/login.keychain-db) + +step(${T_CFG} --preset ${PRESET}) + +if(IPA AND NOT REVIEW) + step(xcodebuild -configuration MinSizeRel -archivePath AusweisApp.xcarchive -scheme AusweisAppBinary -destination "generic/platform=${PLATFORM}" archive CHDIR ${T_BUILD_DIR}) + step(xcodebuild -configuration MinSizeRel -archivePath AusweisApp.xcarchive -exportArchive -exportOptionsPlist exportOptions.plist -exportPath . CHDIR ${T_BUILD_DIR}) +else() + step(xcodebuild -configuration MinSizeRel -scheme AusweisAppBinary -destination "generic/platform=${PLATFORM}" CHDIR ${T_BUILD_DIR}) +endif() + +if(RELEASE) + step(${CMAKE_COMMAND} -E tar cf AusweisApp_BuildDir.tar.zstd --zstd build) +endif() + +step(xcodebuild -configuration MinSizeRel -target ${TARGET} CHDIR ${T_BUILD_DIR}) + +if(IPA AND NOT RELEASE) + step(${T_CTEST} -C MinSizeRel) +endif() + +if(DEFINED ENV{JENKINS_HOME} AND IPA AND RELEASE AND DEFINED ENV{USE_DISTRIBUTION_PROFILE}) + if($ENV{USE_DISTRIBUTION_PROFILE}) + include(${CMAKE_CI_DIR}/Deploy.cmake/IPA.cmake) + endif() +endif() diff -Nru ausweisapp2-2.3.1/ci/import.py ausweisapp2-2.4.0/ci/import.py --- ausweisapp2-2.3.1/ci/import.py 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/import.py 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,177 @@ +#!/usr/bin/env python + +import hglib +from hglib.util import b + +import argparse +import os +import glob +import sys + +from pathlib import Path + + +class CommandLineParser(argparse.ArgumentParser): + def __init__(self): + super().__init__() + self.add_argument( + '--clean', help='Clean up patches', action='store_true' + ) + self.add_argument( + '--clean-only', help='Clean up only', action='store_true' + ) + self.add_argument( + '--clean-dir-only', + help='Clean up working directory only', + action='store_true', + ) + self.add_argument( + '--split', help='Apply patches split', action='store_true' + ) + self.add_argument( + '--repatch', help='Repatch previous series', action='store_true' + ) + self.add_argument( + '--patch', help='Patch file', default='../patch.diff' + ) + self.args = self.parse_args() + + for key, value in vars(self.args).items(): + setattr(self, key, value) + + +class PatchProcessor: + def __init__(self, file, repatch): + self.file = file.decode('utf-8') + self.dir = os.path.dirname(os.path.realpath(self.file)) + self.patch_series = sorted( + glob.glob(self.dir + os.sep + '*.patch'), + key=lambda x: int(Path(x).stem), + ) + self.applied = self.dir + os.sep + 'applied' + self.applied_patches = [] + + if os.path.isfile(self.applied): + with open(self.applied, 'r') as f: + self.applied_patches = list(filter(None, f.read().split('\n'))) + if repatch and len(self.applied_patches) > 0: + self.applied_patches.pop() + + def clean(self): + if os.path.isfile(self.applied): + os.remove(self.applied) + for patch in self.patch_series: + os.remove(patch) + + self.patch_series = [] + self.applied_patches = [] + + def split(self): + def nextFilename(): + self.patch_series.append( + self.dir + os.sep + str(len(self.patch_series)) + '.patch' + ) + return open(self.patch_series[-1], 'wb') + + with open(self.file, 'rb') as f: + file = nextFilename() + for line in f: + if line == b'# HG changeset patch\n' and file.tell() > 0: + file.close() + file = nextFilename() + file.write(line) + file.close() + + def isSplit(self): + return len(self.patch_series) > 0 + + def apply(self, client, split): + if self.patch_series == self.applied_patches or not split: + print('Apply all patches: %s' % len(self.patch_series)) + client.import_( + [s.encode('utf-8') for s in self.patch_series], nocommit=True + ) + return + + with open(self.applied, 'w') as f: + for patch in self.patch_series: + pending = patch not in self.applied_patches + print('Apply patch: {} (pending: {})'.format(patch, pending)) + client.import_( + [patch.encode('utf-8')], + user='CI', + date='today', + message=patch, + nocommit=pending, + ) + + f.write(patch) + f.write('\n') + if pending: + print('Pending patch: {}'.format(patch)) + break + + +def main(): + parser = CommandLineParser() + + patch = b(parser.patch) + if not os.path.isfile(patch): + print('Patch file "{}" does not exists'.format(patch)) + return -1 + + if 'CI' not in os.environ: + print('Warning: This script should be called on CI only') + return -1 + + cfg = [ + 'extensions.hgext.purge=', + 'extensions.hgext.strip=', + 'patch.eol=auto', + 'phases.new-commit=draft', + ] + client = hglib.open(configs=cfg) + + rev = client.identify(id=True).replace(b('+'), b('')).rstrip() + print('Revert workspace to: ' + str(rev)) + client.update(rev=rev, clean=True) + + print('Purge workspace...') + client.rawcommand([b('purge'), b('--all')]) + + if parser.clean_dir_only: + return 0 + + revs = len(client.log(revrange='secret() or draft()')) + print('Found secret/draft changesets: {}'.format(revs)) + + if revs > 0: + print('Strip secret and draft changesets...') + client.rawcommand( + [ + b('strip'), + b('-r'), + b('secret() or draft()'), + b('--no-backup'), + b('--force'), + ] + ) + + processor = PatchProcessor(patch, parser.repatch) + if parser.clean or parser.clean_only: + processor.clean() + + if parser.clean_only: + return 0 + + if not processor.isSplit(): + print('Split patch: {}'.format(patch)) + processor.split() + print('Wrote {} patch(es)'.format(len(processor.patch_series))) + + processor.apply(client, parser.split) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff -Nru ausweisapp2-2.3.1/ci/pipeline.py ausweisapp2-2.4.0/ci/pipeline.py --- ausweisapp2-2.3.1/ci/pipeline.py 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/pipeline.py 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,428 @@ +#!/usr/bin/env python + +import argparse +import hashlib +import json +import logging +import os +import subprocess +import sys + +import gitlab +import requests + +log = logging.getLogger('gitlab') +logging.basicConfig(format='%(levelname)s: %(message)s', level='INFO') + +stdout_handler = logging.StreamHandler(sys.stdout) +stdout_handler.setFormatter(logging.Formatter('%(message)s')) +log_stdout = logging.getLogger('gitlab.stdout') +log_stdout.setLevel(logging.INFO) +log_stdout.addHandler(stdout_handler) +log_stdout.propagate = False + +parser = None + + +class CommandLineParser(argparse.ArgumentParser): + def __init__(self): + self.regex = '.*' + self.api_path = '/api/v4/projects/' + + prog = os.path.basename(sys.argv[0]) + description = f"""\ +Trigger a GitLab CI/CD pipeline with optional patches, revisions +and environment variables. It supports submitting Mercurial patches, +triggering release pipelines and selecting specific jobs via regex. + + +# Typical workflows + +- Specific jobs for current draft changesets: + {prog} -p -a Android + +- Pipeline with current draft changesets as single patch: + {prog} -p -s + +- Run only selected jobs using a regex: + {prog} -a SonarQube -r default + +- Pipeline using a branch as revision: + {prog} -r default + +- Pipeline with a specific patch changeset on specific branch: + {prog} -p tip -r default + +- Release pipeline (requires --rev): + {prog} --release -r 1.0 + + +# Helper functionality (does not trigger a pipeline) + +- Show secure files of the GitLab project: + {prog} --files +""" + + epilog = """\ +notes: + - The token can be provided via the CI_PIPELINE_TOKEN environment variable. +""" + + super().__init__( + description=description, + epilog=epilog, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + self.add_argument( + '--release', help='Create release pipeline', action='store_true' + ) + self.add_argument('-r', '--rev', help='Use revision for the pipeline') + self.add_argument( + '-p', + '--patch', + help='Use patches for the pipeline', + nargs='?', + const='::. and draft()', + ) + self.add_argument( + '-s', + '--squash', + help='Squash multiple changesets into a single diff', + action='store_true', + ) + self.add_argument( + '-a', + '--apply', + help='Apply regex to select jobs for the pipeline', + ) + self.add_argument( + '-m', + '--manual', + help='Enable "manual" builds of non-matching jobs', + action='store_true', + ) + self.add_argument( + '-e', + '--env', + help='Add environment variable to the pipeline', + action='append', + ) + self.add_argument( + '--dry-run', + help='Dry-run instead of creating a pipeline', + action='store_true', + ) + self.add_argument('--repository', help='Use local repository') + self.add_argument( + '--files', + help='Show list or download of secure files and exit', + nargs='?', + const=' ', + ) + self.add_argument( + '--packages', + help='Show list of packages and exit', + nargs='?', + const=' ', + ) + self.add_argument( + '--show', + help='Show list of pipelines and exit', + nargs='?', + const='running', + ) + self.add_argument( + '--token', + help='Use token or provide CI_PIPELINE_TOKEN environment variable', + ) + self.add_argument( + '--gitlab', help='URL of gitlab', default='https://gitlab.govkg.de' + ) + self.add_argument( + '--project', help='ID of gitlab project', default='786' + ) + self.add_argument( + '--ref', help='Git ref of gitlab project', default='main' + ) + self.add_argument( + '-v', '--verbose', help='Verbose output', action='store_true' + ) + self.args = self.parse_args() + + if self.args.files and (self.args.rev or self.args.patch): + self.error('Cannot combine --files') + if self.args.show and (self.args.rev or self.args.patch): + self.error('Cannot combine --show') + if self.args.files and self.args.show: + self.error('Cannot combine --files and --show') + if self.args.release and self.args.patch: + self.error('Cannot combine --release and --patch') + if self.args.release and not self.args.rev: + self.error('Provide revision for the release') + if ( + not self.args.release + and not self.args.patch + and not self.args.rev + and not self.args.files + and not self.args.show + and not self.args.packages + ): + self.error('Provide revision for the pipeline') + if not self.args.token: + self.args.token = os.environ.get('CI_PIPELINE_TOKEN') + if not self.args.token: + self.error('Token is required') + + for key, value in vars(self.args).items(): + setattr(self, key, value) + + def headers(self): + return {'PRIVATE-TOKEN': self.token} + + +def get_url(): + return parser.gitlab + parser.api_path + parser.project + + +def load_hgrc(): + hgrc = {} + if output := run(['hg', 'showconfig']): + for line in output.stdout.strip().splitlines(): + line = line.split('=', 1) + if len(line) == 2: + key, value = line + else: + key = line[0] + value = '' + hgrc[key] = value.strip() + return hgrc + + +def run(cmd): + try: + return subprocess.run( + cmd, + capture_output=True, + text=True, + check=True, + cwd=parser.repository, + encoding='utf-8', + ) + except subprocess.CalledProcessError as e: + log.error(e.stderr.strip()) + return None + + +def export(): + if parser.patch == 'w': + return run(['hg', 'diff', '--nodates', '-g']) + else: + return run(['hg', 'export', '--nodates', '-g', '-r', parser.patch]) + + +def patch(): + if not parser.repository: + parser.repository = load_hgrc().get( + 'bundle.mainreporoot' + ) or os.path.dirname(os.path.realpath(__file__)) + log.debug('Use working directory: %s', parser.repository) + result = export() + + if not result or not result.stdout.strip(): + log.error('Patch is empty or cannot be generated') + return None + + urlInfo = upload(result.stdout) + if urlInfo is not None and parser.rev is None: + branch = run(['hg', 'branch']) + if branch is None: + return None + parser.rev = branch.stdout.strip() + + return urlInfo + + +def upload(content): + file = hashlib.md5(content.encode('utf-8')).hexdigest() + url = get_url() + f'/packages/generic/patches/drafts/{file}.patch' + if parser.dry_run: + log.info(content) + log.info(f'Upload patch: {url}') + else: + try: + response = requests.head(url, headers=parser.headers()) + + if response.status_code == 404: + response = requests.put( + url, headers=parser.headers(), data=content + ) + if response.status_code != 201: + log.error( + 'Upload patch file failed: ' + f'{response.status_code} {response.text}' + ) + return None + except Exception as e: + log.error(f'Cannot upload patch file: {e}') + return None + + identifier = f'{parser.patch} / {file}' + return [ + {'key': 'PATCH_URL', 'value': url}, + {'key': 'PATCH_IDENTIFIER', 'value': identifier}, + ] + + +def pipeline(variables, inputs): + try: + gl = gitlab.Gitlab(parser.gitlab, private_token=parser.token) + if parser.verbose: + gl.enable_debug() + project = gl.projects.get(parser.project, lazy=True) + pipeline = project.pipelines.create( + {'ref': parser.ref, 'variables': variables, 'inputs': inputs} + ) + log.info(f'Pipeline started: {pipeline.web_url}') + except Exception as e: + log.error(f'Cannot start pipeline: {e}') + + +def api(url, stdout=True): + log.debug(url) + try: + response = requests.get(url, headers=parser.headers()) + log.debug(f'HTTP Code: {response.status_code}') + log.debug(json.dumps(dict(response.headers), indent=2)) + if 'application/json' in response.headers.get('Content-Type'): + content = json.dumps(response.json(), indent=2) + else: + content = response.text + if stdout: + log_stdout.info(content) + + if response.status_code > 400: + raise requests.HTTPError(f'HTTP Error {response.status_code}') + + return (response.headers, content) + except Exception as e: + log.error(f'Cannot fetch api {url}: {e}') + return (None, None) + + +def secure_files(): + url = get_url() + '/secure_files/' + if parser.files.strip(): + url += parser.files + '/download' + headers, _ = api(url) + return headers is not None + + +def show(): + url = get_url() + if parser.show.isdigit(): + url += f'/pipelines/{parser.show}/' + else: + url += f'/pipelines?status={parser.show}' + headers, _ = api(url) + return headers is not None + + +def packages(): + from datetime import datetime, timezone, timedelta + + now = datetime.now(timezone.utc) + page = '1' + while page: + url = get_url() + f'/packages?page={page}' + (headers, content) = api(url, False) + if not headers: + return 1 + page = headers['X-Next-Page'] + + if content: + for entry in json.loads(content): + tsp = datetime.fromisoformat( + entry.get('last_downloaded_at') or entry.get('created_at') + ) + diff = now - tsp + if diff > timedelta(days=7): + log.info( + f'Package obsolete: {entry["id"]} ' + f'| {entry["version"]} ' + f'| {entry["name"]}' + ) + if parser.packages == 'delete': + log.info('Delete outdated package') + response = requests.delete( + entry['_links']['delete_api_path'], + headers=parser.headers(), + ) + if response.status_code != 204: + log.error( + 'Deletion of package failed: ' + f'{response.status_code} {response.text}' + ) + else: + log.info( + f'Package relevant: {entry["id"]} ' + f'| {entry["version"]} ' + f'| {entry["name"]}' + ) + + +def main(): + global parser + parser = CommandLineParser() + + if parser.verbose: + log.setLevel('DEBUG') + + if parser.files: + return secure_files() + + if parser.show: + return show() + + if parser.packages: + return packages() + + variables = [] + inputs = {} + + if parser.patch: + v = patch() + if not v: + return 1 + variables += v + + inputs['revision'] = parser.rev + if parser.release: + inputs['release'] = True + + if parser.apply: + variables += [ + {'key': 'SELECT_JOB_REGEX', 'value': f'/{parser.apply}/i'} + ] + if parser.squash: + variables += [{'key': 'SPLIT', 'value': 'OFF'}] + + if parser.manual: + variables += [{'key': 'SELECT_JOB_MANUAL', 'value': ''}] + + if parser.env: + for entry in parser.env: + if '=' in entry: + key, value = entry.split('=', 1) + else: + key, value = entry, '1' + variables += [{'key': key, 'value': value}] + + if parser.dry_run: + log.info(variables) + log.info(inputs) + else: + pipeline(variables, inputs) + + +if __name__ == '__main__': + sys.exit(main()) diff -Nru ausweisapp2-2.3.1/ci/playstore.py ausweisapp2-2.4.0/ci/playstore.py --- ausweisapp2-2.3.1/ci/playstore.py 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/playstore.py 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,126 @@ +#!/usr/bin/env python + +import argparse +import logging +import sys + +from google.oauth2 import service_account +from googleapiclient.discovery import build +from googleapiclient.http import MediaFileUpload + +logging.basicConfig(level=logging.ERROR) + + +class CommandLineParser(argparse.ArgumentParser): + def __init__(self): + super().__init__() + self.add_argument('--json', help='Service account file', required=True) + self.add_argument( + '--package', + help='Package name', + default='com.governikus.ausweisapp2', + ) + self.add_argument('--track', help='Release track', default='internal') + self.add_argument( + '--upload', nargs='+', help='List of files (APK, AAB) to upload' + ) + self.add_argument( + '-v', '--verbose', help='Verbose output', action='store_true' + ) + + self.args = self.parse_args() + for key, value in vars(self.args).items(): + setattr(self, key, value) + + +def FetchNextVersionCode(parser, service, edit_id): + track_result = ( + service.edits() + .tracks() + .get(packageName=parser.package, editId=edit_id, track=parser.track) + .execute() + ) + + releases = track_result.get('releases', []) + all_version_codes = [] + for release in releases: + codes = release.get('versionCodes', []) + all_version_codes.extend(int(code) for code in codes) + + if all_version_codes: + print(max(all_version_codes) + 1) + else: + print('0') + + +def UploadFiles(parser, service, edit_id): + versionCodes = [] + + for uploadFile in parser.upload: + if uploadFile.endswith('.apk'): + target = service.edits().apks() + media = MediaFileUpload( + uploadFile, mimetype='application/vnd.android.package-archive' + ) + elif uploadFile.endswith('.aab'): + target = service.edits().bundles() + media = MediaFileUpload( + uploadFile, mimetype='application/octet-stream' + ) + else: + print(f'Unknown file: {uploadFile}') + sys.exit(-1) + + response = target.upload( + editId=edit_id, packageName=parser.package, media_body=media + ).execute() + versionCodes.append(response['versionCode']) + print(f'Version code {response["versionCode"]} has been uploaded') + + response = ( + service.edits() + .tracks() + .update( + editId=edit_id, + track=parser.track, + packageName=parser.package, + body={ + 'releases': [{'versionCodes': versionCodes, 'status': 'draft'}] + }, + ) + .execute() + ) + print(f'Track {response["track"]} is ready') + + commit_request = ( + service.edits() + .commit(editId=edit_id, packageName=parser.package) + .execute() + ) + print(f'Edit {commit_request["id"]} has been committed') + + +def main(): + parser = CommandLineParser() + if parser.verbose: + logging.root.setLevel(logging.DEBUG) + + SCOPES = ['https://www.googleapis.com/auth/androidpublisher'] + credentials = service_account.Credentials.from_service_account_file( + parser.json, scopes=SCOPES + ) + + service = build('androidpublisher', 'v3', credentials=credentials) + result = ( + service.edits().insert(body={}, packageName=parser.package).execute() + ) + edit_id = result['id'] + + if parser.upload: + UploadFiles(parser, service, edit_id) + else: + FetchNextVersionCode(parser, service, edit_id) + + +if __name__ == '__main__': + main() diff -Nru ausweisapp2-2.3.1/ci/presets/android.json ausweisapp2-2.4.0/ci/presets/android.json --- ausweisapp2-2.3.1/ci/presets/android.json 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/presets/android.json 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,46 @@ +{ + "version": 6, + "include": [ + "ci.json" + ], + "configurePresets": [ + { + "name": "ci-android", + "hidden": true, + "inherits": "ci-with-libs", + "toolchainFile": "${sourceDir}/cmake/android.toolchain.cmake" + }, + { + "name": "ci-android-apk", + "inherits": "ci-android", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "MinSizeRel", + "QT_ANDROID_SIGN_APK": "ON", + "QT_ANDROID_SIGN_AAB": "ON" + } + }, + { + "name": "ci-android-apk-review", + "inherits": "ci-android", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "DEBUG" + } + }, + { + "name": "ci-android-aar", + "inherits": "ci-android", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "MinSizeRel", + "INTEGRATED_SDK": "ON" + } + }, + { + "name": "ci-android-aar-review", + "inherits": "ci-android", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "DEBUG", + "INTEGRATED_SDK": "ON" + } + } + ] +} diff -Nru ausweisapp2-2.3.1/ci/presets/bsd.json ausweisapp2-2.4.0/ci/presets/bsd.json --- ausweisapp2-2.3.1/ci/presets/bsd.json 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/presets/bsd.json 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,12 @@ +{ + "version": 6, + "include": [ + "linux.json" + ], + "configurePresets": [ + { + "name": "ci-bsd", + "inherits": "ci-linux" + } + ] +} diff -Nru ausweisapp2-2.3.1/ci/presets/ci.json ausweisapp2-2.4.0/ci/presets/ci.json --- ausweisapp2-2.3.1/ci/presets/ci.json 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/presets/ci.json 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,26 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "ci", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceParentDir}/build", + "cacheVariables": { + "CMAKE_CXX_COMPILER_LAUNCHER": "ccache" + }, + "errors": { + "dev": true, + "deprecated": true + } + }, + { + "name": "ci-with-libs", + "hidden": true, + "inherits": "ci", + "cacheVariables": { + "CMAKE_PREFIX_PATH": "${sourceParentDir}/libs/dist" + } + } + ] +} diff -Nru ausweisapp2-2.3.1/ci/presets/iOS.json ausweisapp2-2.4.0/ci/presets/iOS.json --- ausweisapp2-2.3.1/ci/presets/iOS.json 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/presets/iOS.json 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,40 @@ +{ + "version": 6, + "include": [ + "ci.json" + ], + "configurePresets": [ + { + "name": "ci-ios", + "inherits": "ci-with-libs", + "generator": "Xcode", + "toolchainFile": "${sourceDir}/cmake/iOS.toolchain.cmake", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "MinSizeRel" + } + }, + { + "name": "ci-ios-framework", + "inherits": "ci-ios", + "cacheVariables": { + "INTEGRATED_SDK": "ON" + } + }, + { + "name": "ci-ios-framework-simulator-x86_64", + "inherits": "ci-ios-framework", + "generator": "Xcode", + "cacheVariables": { + "CMAKE_OSX_SYSROOT": "iphonesimulator", + "CMAKE_OSX_ARCHITECTURES": "x86_64" + } + }, + { + "name": "ci-ios-framework-simulator-arm64", + "inherits": "ci-ios-framework-simulator-x86_64", + "cacheVariables": { + "CMAKE_OSX_ARCHITECTURES": "arm64" + } + } + ] +} diff -Nru ausweisapp2-2.3.1/ci/presets/linux.json ausweisapp2-2.4.0/ci/presets/linux.json --- ausweisapp2-2.3.1/ci/presets/linux.json 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/presets/linux.json 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,27 @@ +{ + "version": 6, + "include": [ + "ci.json" + ], + "configurePresets": [ + { + "name": "ci-linux", + "inherits": "ci-with-libs", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "COVERAGE": "ON", + "BUILD_SHARED_LIBS": "ON", + "SANITIZER": "ON" + } + }, + { + "name": "ci-integrated", + "inherits": "ci-linux", + "cacheVariables": { + "INTEGRATED_SDK": "ON", + "BUILD_SHARED_LIBS": "OFF", + "CMAKE_CXX_COMPILER": "clazy" + } + } + ] +} diff -Nru ausweisapp2-2.3.1/ci/presets/macOS.json ausweisapp2-2.4.0/ci/presets/macOS.json --- ausweisapp2-2.3.1/ci/presets/macOS.json 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/presets/macOS.json 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,33 @@ +{ + "version": 6, + "include": [ + "ci.json" + ], + "configurePresets": [ + { + "name": "ci-macos", + "inherits": "ci-with-libs", + "generator": "Xcode", + "cacheVariables": { + "BUILD_SHARED_LIBS": "ON", + "SANITIZER": "ON" + } + }, + { + "name": "ci-macos-release", + "inherits": "ci-with-libs", + "generator": "Xcode", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "MinSizeRel" + } + }, + { + "name": "ci-macos-integrated", + "inherits": "ci-with-libs", + "cacheVariables": { + "INTEGRATED_SDK": "ON", + "SANITIZER": "ON" + } + } + ] +} diff -Nru ausweisapp2-2.3.1/ci/presets/tools.json ausweisapp2-2.4.0/ci/presets/tools.json --- ausweisapp2-2.3.1/ci/presets/tools.json 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/presets/tools.json 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,32 @@ +{ + "version": 6, + "include": [ + "ci.json" + ], + "configurePresets": [ + { + "name": "ci-formatting", + "inherits": "ci-with-libs", + "cacheVariables": { + "UPDATE_TRANSLATIONS": "ON", + "UPDATE_TRANSLATIONS_NO_OBSOLETE": "ON" + } + }, + { + "name": "ci-translations", + "inherits": "ci-formatting", + "cacheVariables": { + "UPDATE_TRANSLATIONS_ADD_DVCS": "ON" + } + }, + { + "name": "ci-tools", + "inherits": "ci", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RELEASE", + "tools.only": "ON", + "CMAKE_CXX_COMPILER_LAUNCHER": null + } + } + ] +} diff -Nru ausweisapp2-2.3.1/ci/presets/windows.json ausweisapp2-2.4.0/ci/presets/windows.json --- ausweisapp2-2.3.1/ci/presets/windows.json 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/presets/windows.json 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,32 @@ +{ + "version": 6, + "include": [ + "ci.json" + ], + "configurePresets": [ + { + "name": "ci-win", + "inherits": "ci-with-libs" + }, + { + "name": "ci-win-release", + "inherits": "ci-win", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "MinSizeRel", + "WIN_SIGN_KEYSTORE": "$env{WIN_SIGN_KEYSTORE}", + "WIN_SIGN_KEYSTORE_PSW": "$env{WIN_SIGN_KEYSTORE_PSW}", + "WIN_SIGN_SUBJECT_NAME": "$env{WIN_SIGN_SUBJECT_NAME}" + } + }, + { + "name": "ci-win-debug", + "inherits": "ci-win", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "DEBUG", + "WIN_SIGN_KEYSTORE": "$env{WIN_SIGN_KEYSTORE}", + "WIN_SIGN_KEYSTORE_PSW": "$env{WIN_SIGN_KEYSTORE_PSW}", + "WIN_SIGN_SUBJECT_NAME": "$env{WIN_SIGN_SUBJECT_NAME}" + } + } + ] +} diff -Nru ausweisapp2-2.3.1/ci/requirements.txt ausweisapp2-2.4.0/ci/requirements.txt --- ausweisapp2-2.3.1/ci/requirements.txt 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/requirements.txt 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,2 @@ +python-gitlab +requests diff -Nru ausweisapp2-2.3.1/ci/sonar-project.properties.in ausweisapp2-2.4.0/ci/sonar-project.properties.in --- ausweisapp2-2.3.1/ci/sonar-project.properties.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ci/sonar-project.properties.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,24 @@ +sonar.projectKey=ausweisapp2 +sonar.projectName=@PROJECT_NAME@ +sonar.projectVersion=@PROJECT_VERSION@ + +sonar.projectBaseDir=@PROJECT_SOURCE_DIR@ +sonar.sources=src +sonar.tests=test + +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.cppunit.reportsPath=@PROJECT_BINARY_DIR@/test +sonar.cfamily.reportingCppStandardOverride=c++17 + +sonar.cfamily.compile-commands=@PROJECT_BINARY_DIR@/compile_commands.json +#sonar.cfamily.gcov.reportsPath=@PROJECT_BINARY_DIR@/Testing/CoverageInfo +sonar.coverageReportPaths=@PROJECT_BINARY_DIR@/gcovr_sonarqube.xml +sonar.exclusions=src/external/**,utils/**,**/CMakeFiles/*,**/*.java + +sonar.dependencyCheck.jsonReportPath=@PROJECT_BINARY_DIR@/dependency-check-report.json +sonar.dependencyCheck.htmlReportPath=@PROJECT_BINARY_DIR@/dependency-check-report.html diff -Nru ausweisapp2-2.3.1/ci.cmake ausweisapp2-2.4.0/ci.cmake --- ausweisapp2-2.3.1/ci.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/ci.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION 3.25) ########################################### #### Usage: cmake -P ci.cmake -- cmake args @@ -8,14 +8,11 @@ message(FATAL_ERROR "Script mode is required") endif() -get_filename_component(CMAKE_SOURCE_DIR "${CMAKE_SCRIPT_MODE_FILE}" DIRECTORY) +cmake_path(GET CMAKE_SCRIPT_MODE_FILE PARENT_PATH CMAKE_SOURCE_DIR) message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}") message(STATUS "CMAKE_SOURCE_DIR: ${CMAKE_SOURCE_DIR}") message(STATUS "CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") -set(CMAKE_DIR "${CMAKE_SOURCE_DIR}/cmake") -set(CMAKE_MODULE_PATH "${CMAKE_DIR}/ci") - if(CMAKE_COMMAND MATCHES " ") set(CMAKE_COMMAND cmake) endif() @@ -23,7 +20,15 @@ set(CMAKE_CTEST_COMMAND ctest) endif() -set(IMPORT_PY ${CMAKE_SOURCE_DIR}/resources/jenkins/import.py) +set(IMPORT_PY ${CMAKE_SOURCE_DIR}/ci/import.py) + +if(NOT DEFINED SPLIT) + if(DEFINED ENV{SPLIT}) + set(SPLIT $ENV{SPLIT}) + else() + set(SPLIT ON) + endif() +endif() function(GET_PARAMS _params _definitions) @@ -49,17 +54,26 @@ endfunction() -function(CALC_CHECKSUM _out_checksum _out_repatch) +function(CALC_CHECKSUM _out_checksum) set(CHECKSUM_FILES ${CMAKE_SCRIPT_MODE_FILE} ${IMPORT_PY}) + if(ARGN) + list(APPEND CHECKSUM_FILES ${ARGN}) + endif() + foreach(file ${CHECKSUM_FILES}) file(MD5 "${file}" _hash) string(MD5 hashes "${_hash}${hashes}") endforeach() - if((NOT DEFINED SPLITTED OR SPLITTED) AND PATCHED AND NOT ${_out_checksum} STREQUAL hashes) + set(${_out_checksum} ${hashes} PARENT_SCOPE) +endfunction() + + +function(CALC_REPATCH_CHECKSUM _out_checksum _out_repatch) + CALC_CHECKSUM(hashes) + if(SPLIT AND PATCHED AND NOT ${_out_checksum} STREQUAL hashes) set(${_out_repatch} ON PARENT_SCOPE) endif() - set(${_out_checksum} ${hashes} PARENT_SCOPE) endfunction() @@ -77,7 +91,7 @@ function(STEP) set(options NO_ECHO) - set(oneValueArgs CHDIR OUTPUT RESULT PATH) + set(oneValueArgs CHDIR OUTPUT RESULT PATH INPUT_FILE OUTPUT_FILE) set(multiValueArgs ENV) cmake_parse_arguments(_PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -92,7 +106,7 @@ list(APPEND ENV_CMD --) endif() - if(NOT _PARAM_CHDIR) + if(NOT _PARAM_CHDIR OR (ECHO AND NOT _PARAM_NO_ECHO)) set(_PARAM_CHDIR ${CMAKE_BINARY_DIR}) endif() @@ -101,6 +115,14 @@ set(OUTPUT _output) endif() + if(_PARAM_INPUT_FILE) + set(INPUT_FILE_OPTION INPUT_FILE) + endif() + + if(_PARAM_OUTPUT_FILE) + set(OUTPUT_FILE_OPTION OUTPUT_FILE) + endif() + if(_PARAM_NO_ECHO OR NOT ECHO) set(COMMAND_ECHO STDOUT) else() @@ -120,6 +142,8 @@ execute_process(COMMAND ${ECHO_CMD} ${ENV_CMD} ${_PARAM_UNPARSED_ARGUMENTS} RESULT_VARIABLE _result ${OUTPUT_OPTION} ${OUTPUT} + ${INPUT_FILE_OPTION} ${_PARAM_INPUT_FILE} + ${OUTPUT_FILE_OPTION} ${_PARAM_OUTPUT_FILE} ECHO_OUTPUT_VARIABLE WORKING_DIRECTORY ${_PARAM_CHDIR} COMMAND_ECHO ${COMMAND_ECHO}) @@ -152,34 +176,72 @@ step(${CMAKE_COMMAND} -E rm -r ${T_BUILD_DIR}) endif() - message(STATUS "Use SCRIPT: ${SCRIPT}") - include(${SCRIPT}) + if(NOT EXISTS "${CACHE_DIR}") + file(MAKE_DIRECTORY "${CACHE_DIR}") + endif() + + cmake_path(RELATIVE_PATH SCRIPT BASE_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE SCRIPT_PATH) + message(STATUS "Use SCRIPT: ${SCRIPT_PATH}") + block() + include(${SCRIPT}) + endblock() endfunction() +function(fetch_script _out_script _out_path _path _names) + file(GLOB scripts "${_path}/*.cmake") + foreach(part ${_names}) + foreach(entry ${scripts}) + cmake_path(GET entry STEM filename) + if(part STREQUAL filename) + if(IS_DIRECTORY "${entry}") + fetch_script(script ${_out_path} "${entry}" "${_names}") + else() + set(script ${entry}) + endif() + + if(script) + set(${_out_script} ${script} PARENT_SCOPE) + if(IS_DIRECTORY "${entry}") + set(${_out_path} ${${_out_path}} ${entry} PARENT_SCOPE) + endif() + return() + endif() + endif() + endforeach() + endforeach() +endfunction() macro(PARSE_FROM_NAME) if(DEFINED ENV{JOB_NAME} AND NOT NAME) set(NAME $ENV{JOB_NAME}) endif() + if(DEFINED ENV{CI_JOB_NAME} AND NOT NAME) + set(NAME $ENV{CI_JOB_NAME}) + endif() + if(NOT DEFINED ENV{CI} AND NOT NAME) + set(NAME ${CMAKE_HOST_SYSTEM_NAME}) + message(STATUS "Automatic detection for developer: ${NAME}") + endif() if(NAME) + string(REGEX MATCHALL "[A-Za-z0-9.-]+" NAMES "${NAME}") message(STATUS "Use NAME: ${NAME}") + message(STATUS "Use NAMES: ${NAMES}") - if(NAME MATCHES "^Release_" AND NOT DEFINED RELEASE) - set(RELEASE ON) - elseif(NAME MATCHES "_Review_" AND NOT DEFINED REVIEW) + if((NAME MATCHES "_Review_" OR DEFINED ENV{REVIEWBOARD_REVIEW_ID} OR DEFINED ENV{PATCH_URL}) AND NOT DEFINED REVIEW) set(REVIEW ON) + elseif((NAME MATCHES "^Release_" OR DEFINED ENV{RELEASE}) AND NOT DEFINED RELEASE) + if(DEFINED ENV{GITLAB_CI}) + set(RELEASE $ENV{RELEASE}) + else() + set(RELEASE ON) + endif() endif() if(NOT SCRIPT) - file(GLOB scripts "${CMAKE_MODULE_PATH}/*.cmake") - foreach(entry ${scripts}) - get_filename_component(entry "${entry}" NAME_WLE) - if(NAME MATCHES "_${entry}") - set(SCRIPT ${entry}) - break() - endif() - endforeach() + set(CMAKE_CI_DIR "${CMAKE_SOURCE_DIR}/ci/cmake") + fetch_script(SCRIPT CMAKE_MODULE_PATH "${CMAKE_CI_DIR}" "${NAMES}") + list(APPEND CMAKE_MODULE_PATH ${CMAKE_CI_DIR}) endif() endif() @@ -192,45 +254,89 @@ macro(SET_TEMPLATES) # Provide some base templates for SCRIPTs if(DEFINED ENV{WORKSPACE}) set(WORKSPACE $ENV{WORKSPACE}) + elseif(DEFINED ENV{CI_PROJECT_DIR}) + set(WORKSPACE $ENV{CI_PROJECT_DIR}) else() set(WORKSPACE ${CMAKE_BINARY_DIR}) endif() - set(T_BUILD_DIR build) + if(DEFINED ENV{CACHE_DIR}) + set(CACHE_DIR $ENV{CACHE_DIR}) + else() + set(CACHE_DIR ${WORKSPACE}/cache) + endif() + + set(T_TEMP "${WORKSPACE}/tmp") + set(ENV{TMP} "${T_TEMP}") + set(ENV{TEMP} "${T_TEMP}") + set(ENV{TMPDIR} "${T_TEMP}") + + if(NOT EXISTS "${T_TEMP}") + file(MAKE_DIRECTORY "${T_TEMP}") + endif() + + set(CMAKE_DIR "${CMAKE_SOURCE_DIR}/cmake") + if(DEFINED LIBS) + set(T_LIBS_DIR ${LIBS}) + else() + set(T_LIBS_DIR libs) + endif() + if(DEFINED BUILD) + set(T_BUILD_DIR ${BUILD}) + else() + set(T_BUILD_DIR build) + endif() set(T_DIST_DIR ${T_BUILD_DIR}/dist) set(T_BUILD ${CMAKE_COMMAND} --build ${T_BUILD_DIR}) set(T_TARGET ${T_BUILD} --target) set(T_CTEST ${CMAKE_CTEST_COMMAND} --test-dir ${T_BUILD_DIR} --output-on-failure) - set(T_CFG ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR} -B ${T_BUILD_DIR} ${PARAMS}) + set(T_CMAKE_FRESH ${CMAKE_COMMAND} --fresh) + set(T_CFG ${T_CMAKE_FRESH} ${PARAMS} -S ${CMAKE_SOURCE_DIR} -B ${T_BUILD_DIR}) + set(T_CFG_LIBS ${T_CMAKE_FRESH} ${PARAMS} -S ${CMAKE_SOURCE_DIR}/libs -B ${T_LIBS_DIR}) + set(T_CFG_LIBS_DEPS ${T_CMAKE_FRESH} -S ${CMAKE_SOURCE_DIR}/libs -B ${T_LIBS_DIR}) endmacro() function(hashsum) - foreach(entry ${ARGV}) - file(SHA256 ${entry} fileHash) - get_filename_component(fileName "${entry}" NAME) - file(WRITE ${entry}.sha256 "${fileHash} ${fileName}\n") + set(options NO_FILENAME) + set(multiValueArgs ALGORITHM) + cmake_parse_arguments(_PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT DEFINED _PARAM_ALGORITHM) + set(_PARAM_ALGORITHM SHA256) + endif() + + foreach(entry ${_PARAM_UNPARSED_ARGUMENTS}) + foreach(algo ${_PARAM_ALGORITHM}) + string(TOLOWER "${algo}" fileending) + file(${algo} ${entry} fileHash) + cmake_path(GET entry FILENAME fileName) + + set(hashfile ${entry}.${fileending}) + file(WRITE ${hashfile} "${fileHash}") + if(NOT ${_PARAM_NO_FILENAME}) + file(APPEND ${hashfile} " ${fileName}") + endif() + file(APPEND ${hashfile} "\n") + endforeach() endforeach() endfunction() function(RESPAWN_PATCHED) - if(NOT DEFINED SPLITTED OR SPLITTED) - set(SPLITTED_PARAM --splitted) + if(SPLIT) + set(SPLIT_PARAM --split) endif() if(NOT PATCHED) set(CLEAN_PARAM --clean) endif() - if(DEFINED PENDING AND NOT PENDING AND SPLITTED_PARAM) - set(PENDING_PARAM --no-pending) - endif() if(REPATCH) set(REPATCH_PARAM --repatch) endif() - if(NOT PATCHED OR SPLITTED_PARAM) + if(NOT PATCHED OR SPLIT) unset(PENDING_PATCH CACHE) - IMPORT_PATCH(PENDING_PATCH ${SPLITTED_PARAM} ${CLEAN_PARAM} ${PENDING_PARAM} ${REPATCH_PARAM}) + IMPORT_PATCH(PENDING_PATCH ${SPLIT_PARAM} ${CLEAN_PARAM} ${REPATCH_PARAM}) if(PENDING_PATCH OR NOT PATCHED) set(PATCHED_OPTION "-DPATCHED=ON") @@ -248,14 +354,16 @@ list(FILTER DEFINITIONS EXCLUDE REGEX "${CHECKSUM_OPTION}") list(APPEND DEFINITIONS "${CHECKSUM_OPTION}=${CHECKSUM}") - if(NOT PATCHED AND SPLITTED_PARAM) + if(NOT PATCHED AND SPLIT) set(INITIAL_RUNNER INITIAL_RUNNER_FAILED) endif() message(STATUS "script runner: respawn") step(${CMAKE_COMMAND} ${DEFINITIONS} -P ${CMAKE_SCRIPT_MODE_FILE} -- ${PARAMS} NO_ECHO RESULT ${INITIAL_RUNNER}) if(INITIAL_RUNNER) - IMPORT_PATCH(_unused --clean-only) + if(CLEAN OR DEFINED ENV{JENKINS_HOME}) + IMPORT_PATCH(_unused --clean-only) + endif() if(INITIAL_RUNNER_FAILED) message(FATAL_ERROR "script runner: failed") else() @@ -270,13 +378,28 @@ function(RUN) PARSE_FROM_NAME() GET_PARAMS(PARAMS DEFINITIONS) - CALC_CHECKSUM(CHECKSUM REPATCH) + CALC_REPATCH_CHECKSUM(CHECKSUM REPATCH) if(NOT REPATCH AND (NOT REVIEW OR PATCHED)) CALL_SCRIPT() endif() if(REVIEW) + if(NOT PATCHED AND DEFINED ENV{GITLAB_CI}) + include(Files) + set(patch.diff "${CMAKE_BINARY_DIR}/patch.diff") + if(DEFINED ENV{PATCH_URL}) + step(uv pip install python-hglib) + handle_http("${patch.diff}" URLS $ENV{PATCH_URL}) + elseif(DEFINED ENV{REVIEWBOARD_REVIEW_ID}) + step(uv pip install rbtools python-hglib) + step(rbt patch --write ${patch.diff} --server $ENV{REVIEWBOARD_SERVER} --diff-revision $ENV{REVIEWBOARD_DIFF_REVISION} $ENV{REVIEWBOARD_REVIEW_ID}) + elseif(EXISTS "${patch.diff}") + message(STATUS "Use existing patch: ${patch.diff}") + else() + message(FATAL_ERROR "Unknown patch source") + endif() + endif() RESPAWN_PATCHED() endif() endfunction() diff -Nru ausweisapp2-2.3.1/cmake/Appcast.cmake ausweisapp2-2.4.0/cmake/Appcast.cmake --- ausweisapp2-2.3.1/cmake/Appcast.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Appcast.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -if(MAC OR LINUX OR WIN32) - set(APPCAST_URL https://updates.autentapp.de) - - macro(ADD_APPCAST_FILE _files _system _min) - string(TIMESTAMP APPCAST_DATE "%Y-%m-%dT%H:%M:%S") - - foreach(filePath ${_files}) - file(SIZE ${filePath} fileSize) - get_filename_component(file ${filePath} NAME) - - if(NOT DEFINED fileSize) - message(FATAL_ERROR "Cannot get file size of: ${file}") - endif() - - message(STATUS "Processing: ${file}") - file(READ ${PACKAGING_DIR}/updater/Appcast.item.json.in item) - - string(REPLACE "AusweisApp-" "" APPCAST_FILE_VERSION ${file}) - string(REPLACE ".msi" "" APPCAST_FILE_VERSION ${APPCAST_FILE_VERSION}) - - string(REPLACE "APPCAST_DATE" "${APPCAST_DATE}" item ${item}) - string(REPLACE "APPCAST_PLATFORM" ${_system} item ${item}) - string(REPLACE "APPCAST_MINIMUM_PLATFORM" ${_min} item ${item}) - string(REPLACE "APPCAST_VERSION" "${APPCAST_FILE_VERSION}" item ${item}) - string(REPLACE "APPCAST_URL" "${APPCAST_URL}/${file}" item ${item}) - string(REPLACE "APPCAST_SIZE" "${fileSize}" item ${item}) - string(REPLACE "APPCAST_CHECKSUM" "${APPCAST_URL}/${file}.sha256" item ${item}) - string(REPLACE "APPCAST_NOTES" "${APPCAST_URL}/ReleaseNotes.html#${APPCAST_FILE_VERSION}" item ${item}) - - set(APPCAST_ITEMS "${APPCAST_ITEMS}${item},") - endforeach() - endmacro() - - if(LINUX OR MAC) - file(GLOB MSI_FILES ${PROJECT_BINARY_DIR}/*.msi) - if(MSI_FILES) - ADD_APPCAST_FILE("${MSI_FILES}" "win" "10") - endif() - - if(APPCAST_ITEMS) - string(REGEX REPLACE ",$" "" APPCAST_ITEMS "${APPCAST_ITEMS}") - configure_file(${PACKAGING_DIR}/updater/Appcast.json.in ${PROJECT_BINARY_DIR}/AppcastInfo.json @ONLY) - endif() - endif() - -endif() diff -Nru ausweisapp2-2.3.1/cmake/CompilerFlags.cmake ausweisapp2-2.4.0/cmake/CompilerFlags.cmake --- ausweisapp2-2.3.1/cmake/CompilerFlags.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/CompilerFlags.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,26 +1,24 @@ add_definitions(-DUNICODE) add_definitions(-DQT_MESSAGELOGCONTEXT) -add_definitions(-DQT_NO_CAST_FROM_BYTEARRAY) add_definitions(-DQT_NO_CAST_FROM_ASCII) -add_definitions(-DQT_NO_CAST_TO_ASCII) -add_definitions(-DQT_NO_FOREACH) add_definitions(-DQT_NO_KEYWORDS) add_definitions(-DQT_NO_EXCEPTIONS) -add_definitions(-DQT_NO_CONTEXTLESS_CONNECT) -if(QT_VERSION VERSION_GREATER_EQUAL "6.8" AND QT_VERSION VERSION_LESS "6.10") - add_definitions(-DQT_USE_NODISCARD_FILE_OPEN) -endif() - -if(QT_VENDOR STREQUAL "Governikus") - add_definitions(-DGOVERNIKUS_QT) - add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060502) +if(LIBS_GOVERNIKUS) + add_definitions(-DLIBS_GOVERNIKUS) + version_to_hex(STRICT_UP_TO "${LIBS_QT}") +else() + version_to_hex(STRICT_UP_TO "${MIN_QT_VERSION}") endif() +add_definitions(-DQT_ENABLE_STRICT_MODE_UP_TO=${STRICT_UP_TO}) +message(STATUS "QT_ENABLE_STRICT_MODE_UP_TO: ${STRICT_UP_TO}") get_property(cxx_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES) -if("cxx_std_20" IN_LIST cxx_features) +if (CMAKE_CXX_STANDARD) + #pass +elseif("cxx_std_20" IN_LIST cxx_features) if(MINGW AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "8") set(CMAKE_CXX_STANDARD 17) else() @@ -29,6 +27,10 @@ elseif("cxx_std_17" IN_LIST cxx_features) set(CMAKE_CXX_STANDARD 17) else() + set(CMAKE_CXX_STANDARD 0) +endif() + +if(CMAKE_CXX_STANDARD LESS 17) message(FATAL_ERROR "At least C++17 is required") endif() @@ -45,9 +47,6 @@ if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W3") - if(CMAKE_COMPILE_WARNING_AS_ERROR AND CMAKE_VERSION VERSION_LESS "3.24") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -WX") - endif() ADD_FLAG(/Qspectre) ADD_FLAG(/GS) ADD_FLAG(/DYNAMICBASE) @@ -67,17 +66,8 @@ 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 ANDROID_NDK_REVISION VERSION_LESS "22") - if (CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") - set(PREFER_LD bfd CACHE STRING "") - else() - set(PREFER_LD gold CACHE STRING "") - endif() - else() - set(PREFER_LD lld CACHE STRING "") - set(FALLBACK_LD -fuse-ld=gold) - endif() - + set(PREFER_LD mold CACHE STRING "") + set(FALLBACK_LD -fuse-ld=lld) ADD_FLAG(-fuse-ld=${PREFER_LD} ${FALLBACK_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) @@ -127,29 +117,21 @@ ADD_FLAG(-Wconversion) ADD_FLAG(-Wno-gnu-zero-variadic-macro-arguments) # Qt (qDebug) is not compatible - if(ANDROID OR (INTEGRATED_SDK AND (NOT BUILD_SHARED_LIBS OR NOT BUILD_TESTING))) + if(ANDROID OR (INTEGRATED_SDK AND (NOT BUILD_SHARED_LIBS OR NOT CMAKE_BUILD_TYPE STREQUAL "DEBUG"))) set(CMAKE_CXX_VISIBILITY_PRESET hidden) endif() if(ANDROID) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now") - if(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a" OR CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64") - set(ANDROID_PAGE_SIZE_FLAGS "-Wl,-z,max-page-size=16384") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${ANDROID_PAGE_SIZE_FLAGS}") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${ANDROID_PAGE_SIZE_FLAGS}") - endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finline-limit=64") endif() endif() - if(CMAKE_COMPILE_WARNING_AS_ERROR AND CMAKE_VERSION VERSION_LESS "3.24") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") - endif() - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") + option(SANITIZER "Enable sanitizer" OFF) if(SANITIZER) if(NOT SANITIZER_SKIP_ASAN) set(SANITIZER_FLAGS "-fsanitize=address") @@ -167,7 +149,7 @@ if(CMAKE_CXX_COMPILER MATCHES "clazy") set(clazy_level level0,level1,level2) set(clazy_extra_warnings isempty-vs-count,signal-with-return-value,tr-non-literal,detaching-member) - set(clazy_disabled_warnings no-use-static-qregularexpression,no-copyable-polymorphic,no-ctor-missing-parent-argument,no-fully-qualified-moc-types,no-function-args-by-value) + set(clazy_disabled_warnings no-no-module-include,no-use-static-qregularexpression,no-copyable-polymorphic,no-ctor-missing-parent-argument,no-fully-qualified-moc-types,no-function-args-by-value) set(clazy_all "${clazy_level},${clazy_extra_warnings},${clazy_disabled_warnings}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -plugin-arg-clazy -Xclang ${clazy_all}") @@ -191,11 +173,6 @@ if(CMAKE_CXX_FLAGS MATCHES "-Wnewline-eof") list(APPEND INCOMPATIBLE_QT_COMPILER_FLAGS "-Wno-newline-eof") endif() - - # QTBUG-114897 - if(QT_VERSION VERSION_LESS "6.5.3" AND CMAKE_CXX_FLAGS MATCHES "-Wshadow") - list(APPEND INCOMPATIBLE_QT_COMPILER_FLAGS "-Wno-shadow") - endif() endif() diff -Nru ausweisapp2-2.3.1/cmake/DVCS.cmake ausweisapp2-2.4.0/cmake/DVCS.cmake --- ausweisapp2-2.3.1/cmake/DVCS.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/DVCS.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,3 +1,5 @@ +include(Helper) + macro(FIND_DVCS _dest) if(EXISTS "${_dest}/.hg") FIND_HOST_PACKAGE(Hg) @@ -20,7 +22,7 @@ function(DVCS_EXECUTE _out) execute_process(COMMAND ${DVCS_EXECUTABLE} ${ARGN} - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE _output RESULT_VARIABLE _result ERROR_QUIET @@ -77,25 +79,29 @@ endif() if(HG_FOUND) + DVCS_CALL("revision" "-" id -i) + elseif(GIT_FOUND) + DVCS_CALL("revision" "-" rev-parse --verify --short HEAD) + endif() + + if(HG_FOUND) DVCS_CALL("phase" "" log -r . -T {phase}) if(DEFINED dvcs_phase) if("${dvcs_phase}" STREQUAL "public") - unset(dvcs_phase) + if("${dvcs_revision}" MATCHES "\\+") + set(dvcs_phase "draft") + else() + unset(dvcs_phase) + endif() else() set(VERSION_DVCS ${VERSION_DVCS}-${dvcs_phase}) endif() endif() endif() - - if(HG_FOUND) - DVCS_CALL("revision" "-" id -i) - elseif(GIT_FOUND) - DVCS_CALL("revision" "-" rev-parse --verify --short HEAD) - endif() endmacro() set(VERSION_DVCS ${PROJECT_VERSION}) -FIND_DVCS(${PROJECT_SOURCE_DIR}) +FIND_DVCS(${CMAKE_SOURCE_DIR}) if(DVCS_FOUND) option(ENABLE_DVCS "Check consistency of version/tag and get additional revision data" true) if(ENABLE_DVCS) diff -Nru ausweisapp2-2.3.1/cmake/FindPCSC.cmake ausweisapp2-2.4.0/cmake/FindPCSC.cmake --- ausweisapp2-2.3.1/cmake/FindPCSC.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/FindPCSC.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,6 +1,11 @@ if(UNIX AND NOT APPLE AND NOT CYGWIN) - find_package(PkgConfig REQUIRED) - pkg_check_modules(PCSC REQUIRED IMPORTED_TARGET libpcsclite) + if(CMAKE_VERSION VERSION_LESS "4.1.1") + find_package(PkgConfig REQUIRED) + pkg_check_modules(PCSC REQUIRED IMPORTED_TARGET libpcsclite) + else() + cmake_pkg_config(IMPORT libpcsclite REQUIRED NAME PCSC) + get_target_property(PCSC_LIBRARIES PkgConfig::PCSC INTERFACE_LINK_LIBRARIES) + endif() else() add_library(PkgConfig::PCSC INTERFACE IMPORTED) diff -Nru ausweisapp2-2.3.1/cmake/Helper.cmake ausweisapp2-2.4.0/cmake/Helper.cmake --- ausweisapp2-2.3.1/cmake/Helper.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Helper.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -59,11 +59,7 @@ 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() + set(CMAKE_REQUIRED_LINK_OPTIONS ${_PARAM_LINK} ${flagtest}) if(_PARAM_USE_LINKER_ONLY) set(flagtest "") endif() @@ -443,7 +439,7 @@ set(SIGNTOOL_PARAMS sign /v /fd ${WIN_SIGN_HASHALGO} /d AusweisApp /du https://www.ausweisapp.bund.de) if(WIN_SIGN_SUBJECT_NAME) - set(SIGNTOOL_PARAMS ${SIGNTOOL_PARAMS} /n ${WIN_SIGN_SUBJECT_NAME}) + set(SIGNTOOL_PARAMS ${SIGNTOOL_PARAMS} /a /n ${WIN_SIGN_SUBJECT_NAME}) message(STATUS "Files will be signed using: ${WIN_SIGN_SUBJECT_NAME}") else() string(REPLACE "\\" "/" WIN_SIGN_KEYSTORE "${WIN_SIGN_KEYSTORE}") @@ -466,3 +462,19 @@ elseif(APPLE) find_program(SIGNTOOL_CMD codesign) endif() + +function(version_to_hex _out _version) + if(_version MATCHES "^([0-9]+)\\.([0-9]+)$") + string(APPEND _version ".0") + endif() + if(NOT _version MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)") + message(FATAL_ERROR "Version is invalid: '${_version}'") + endif() + + math(EXPR result "((${CMAKE_MATCH_1} << 16) | (${CMAKE_MATCH_2} << 8) | ${CMAKE_MATCH_3})" OUTPUT_FORMAT HEXADECIMAL) + string(LENGTH "${result}" len) + if(len EQUAL 7) + string(REGEX REPLACE "^0x" "0x0" result "${result}") + endif() + set(${_out} "${result}" PARENT_SCOPE) +endfunction() diff -Nru ausweisapp2-2.3.1/cmake/Install.cmake ausweisapp2-2.4.0/cmake/Install.cmake --- ausweisapp2-2.3.1/cmake/Install.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Install.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -45,7 +45,7 @@ # qt qml plugins (fixup_bundle needs to know this to fetch their dependencies) if((WIN32 OR MAC) AND TARGET ${Qt}::Qml) - set(modules QtQuick QtQml Qt) + set(modules QtQuick QtQml Qt QtCore) foreach(entry ${modules}) set(_lib_dir ${QT_INSTALL_ARCHDATA}/qml/${entry}) @@ -70,6 +70,11 @@ FETCH_TARGET_LOCATION(libQuickControls2 "${Qt}::QuickControls2") install(FILES ${libQuickControls2} DESTINATION . COMPONENT Runtime) list(APPEND LIBS ${libQuickControls2}) + + FETCH_TARGET_LOCATION(libQmlCore "${Qt}::QmlCore") + install(FILES ${libQmlCore} DESTINATION . COMPONENT Runtime) + list(APPEND LIBS ${libQmlCore}) + if(TARGET ${Qt}::QmlWorkerScript) FETCH_TARGET_LOCATION(libQmlWorkerScript "${Qt}::QmlWorkerScript") install(FILES ${libQmlWorkerScript} DESTINATION . COMPONENT Runtime) @@ -91,12 +96,7 @@ FETCH_TARGET_LOCATION(pluginGif "${Qt}::QGifPlugin") FETCH_TARGET_LOCATION(pluginJpeg "${Qt}::QJpegPlugin") FETCH_TARGET_LOCATION(platformWin "${Qt}::QWindowsIntegrationPlugin") - - if(QT_VERSION VERSION_LESS 6.7) - FETCH_TARGET_LOCATION(styleWin "${Qt}::QWindowsVistaStylePlugin") - else() - FETCH_TARGET_LOCATION(styleWin "${Qt}::QModernWindowsStylePlugin") - endif() + FETCH_TARGET_LOCATION(styleWin "${Qt}::QModernWindowsStylePlugin") install(TARGETS AusweisAppBinary DESTINATION . COMPONENT Application) install(FILES ${pluginSvg} DESTINATION imageformats COMPONENT Runtime) @@ -148,7 +148,7 @@ install_mac_plugins("${plugins}") if(TARGET ${Qt}::Qml) - foreach(entry QtQuick QtQuick.2 QtQml Qt) + foreach(entry QtQuick QtQuick.2 QtQml Qt QtCore) set(_dir "${QT_INSTALL_ARCHDATA}/qml") file(GLOB_RECURSE DYLIB "${_dir}/${entry}/*.dylib") foreach(_lib ${DYLIB}) diff -Nru ausweisapp2-2.3.1/cmake/Libraries.cmake ausweisapp2-2.4.0/cmake/Libraries.cmake --- ausweisapp2-2.3.1/cmake/Libraries.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Libraries.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,9 +1,15 @@ # Set CMAKE_PREFIX_PATH with toolchain directory -if(DESKTOP) - set(MIN_QT_VERSION 6.4) +find_package(Governikus QUIET) +if(LIBS_GOVERNIKUS) + message(STATUS "Library revision: ${LIBS_REVISION}") + string(REGEX REPLACE "-.*" "" MIN_QT_VERSION "${LIBS_QT}") else() - set(MIN_QT_VERSION 6.8) + if(DESKTOP) + set(MIN_QT_VERSION 6.8) + else() + set(MIN_QT_VERSION 6.9) + endif() endif() if(IOS OR ANDROID) @@ -33,13 +39,13 @@ endif() if(INTEGRATED_SDK) - if(MAC AND CMAKE_BUILD_TYPE STREQUAL "DEBUG") + if(CMAKE_BUILD_TYPE STREQUAL "DEBUG") list(APPEND QT_COMPONENTS Gui) endif() else () - list(APPEND QT_COMPONENTS Svg WebSockets Qml Quick QuickControls2 QuickTemplates2 QmlWorkerScript ShaderTools) + list(APPEND QT_COMPONENTS Svg WebSockets Qml Quick QuickControls2 QuickTemplates2 QmlWorkerScript) if(DESKTOP) - list(APPEND QT_COMPONENTS Widgets) + list(APPEND QT_COMPONENTS Widgets QmlCore) endif() endif() @@ -48,6 +54,9 @@ endif() if(ANDROID) + if(QT_VERSION VERSION_GREATER_EQUAL "6.10") + list(APPEND QT_COMPONENTS CorePrivate) + endif() if(INTEGRATED_SDK) list(APPEND QT_COMPONENTS WebSockets) endif() @@ -61,12 +70,6 @@ message(STATUS "QT_INSTALL_ARCHDATA: ${QT_INSTALL_ARCHDATA}") message(STATUS "QT_INSTALL_TRANSLATIONS: ${QT_INSTALL_TRANSLATIONS}") -set(QT_VENDOR_FILE "${QT_INSTALL_ARCHDATA}/mkspecs/qt_vendor_governikus") -if(EXISTS "${QT_VENDOR_FILE}") - set(QT_VENDOR "Governikus") - message(STATUS "QT_VENDOR: ${QT_VENDOR}") -endif() - if(NOT DEFINED QT_INSTALL_TRANSLATIONS) message(FATAL_ERROR "Cannot detect QT_INSTALL_TRANSLATIONS") endif() @@ -77,7 +80,12 @@ set(CMAKE_CROSSCOMPILING ON) endif() -set(MIN_OPENSSL_VERSION 1.1.1) +if(LIBS_GOVERNIKUS) + set(MIN_OPENSSL_VERSION ${LIBS_OPENSSL}) + string(REGEX REPLACE "[a-z]" "" MIN_OPENSSL_VERSION "${MIN_OPENSSL_VERSION}") +else() + set(MIN_OPENSSL_VERSION 1.1.1) +endif() find_package(OpenSSL ${MIN_OPENSSL_VERSION} REQUIRED) if(tmp_crosscompile_enabled) @@ -121,8 +129,12 @@ find_library(OSX_SERVICEMANAGEMENT ServiceManagement) elseif(UNIX) if(LINUX OR BSD) - find_package(PkgConfig REQUIRED) - pkg_check_modules(UDEV IMPORTED_TARGET libudev) + if(CMAKE_VERSION VERSION_LESS "4.1.1") + find_package(PkgConfig REQUIRED) + pkg_check_modules(UDEV IMPORTED_TARGET libudev) + else() + cmake_pkg_config(IMPORT libudev NAME UDEV) + endif() if(NOT TARGET PkgConfig::UDEV) message(STATUS "Hardware detection disabled - Missing libudev") endif() @@ -140,8 +152,14 @@ endif() list(APPEND ${QT_TEST_COMPONENTS} Test) + if(QT_VERSION VERSION_GREATER_EQUAL "6.10" AND NOT CorePrivate IN_LIST QT_COMPONENTS) + list(APPEND ${QT_TEST_COMPONENTS} CorePrivate) + endif() if(NOT INTEGRATED_SDK) list(APPEND ${QT_TEST_COMPONENTS} QuickTest) + if(LIBS_GOVERNIKUS) + list(APPEND ${QT_TEST_COMPONENTS} QmlCompiler) + endif() endif() if(INTEGRATED_SDK) @@ -150,6 +168,9 @@ endif() if(QT_COMPONENTS) + if(CorePrivate IN_LIST QT_COMPONENTS OR CorePrivate IN_LIST QT_COMPONENTS_OPTIONAL) + set(QT_NO_PRIVATE_MODULE_WARNING ON) + endif() find_package(${Qt} ${MIN_QT_VERSION} REQUIRED COMPONENTS ${QT_COMPONENTS} OPTIONAL_COMPONENTS ${QT_COMPONENTS_OPTIONAL}) endif() diff -Nru ausweisapp2-2.3.1/cmake/Merge.cmake ausweisapp2-2.4.0/cmake/Merge.cmake --- ausweisapp2-2.3.1/cmake/Merge.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Merge.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.25) if(NOT CMAKE_SCRIPT_MODE_FILE) message(FATAL_ERROR "Usage: cmake -P cmake/Merge.cmake") diff -Nru ausweisapp2-2.3.1/cmake/Messages.cmake ausweisapp2-2.4.0/cmake/Messages.cmake --- ausweisapp2-2.3.1/cmake/Messages.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Messages.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -9,6 +9,7 @@ if(APPLE) message(STATUS "CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT}") message(STATUS "CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET}") + message(STATUS "CMAKE_OSX_ARCHITECTURES: ${CMAKE_OSX_ARCHITECTURES}") endif() message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") message(STATUS "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") diff -Nru ausweisapp2-2.3.1/cmake/Notarization.cmake ausweisapp2-2.4.0/cmake/Notarization.cmake --- ausweisapp2-2.3.1/cmake/Notarization.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Notarization.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10.0) +cmake_minimum_required(VERSION 3.25) if(NOT CMAKE_SCRIPT_MODE_FILE) message(STATUS "Usage: cmake -P cmake/Notarization.cmake") @@ -26,8 +26,8 @@ message(STATUS "Using DMG: ${DMG_FILE}") -set(keychain --keychain-profile "AC_PASSWORD") -execute_process(COMMAND ${XCRUN} notarytool submit ${keychain} ${DMG_FILE} OUTPUT_VARIABLE UUID_OUTPUT) +set(credentials --apple-id $ENV{APPSTORE_USER} --password $ENV{APPSTORE_PSW} --team-id $ENV{APPSTORE_TEAM}) +execute_process(COMMAND ${XCRUN} notarytool submit ${credentials} ${DMG_FILE} OUTPUT_VARIABLE UUID_OUTPUT) set(regex_uuid "id: ([-|0-9|a-z]+)") FETCH_REGEX(UUID "${regex_uuid}" "${UUID_OUTPUT}") @@ -38,9 +38,9 @@ endif() message(STATUS "Wait...") -execute_process(COMMAND ${XCRUN} notarytool wait ${UUID} ${keychain}) +execute_process(COMMAND ${XCRUN} notarytool wait ${UUID} ${credentials}) -execute_process(COMMAND ${XCRUN} notarytool info ${UUID} ${keychain} OUTPUT_VARIABLE STATUS_OUTPUT) +execute_process(COMMAND ${XCRUN} notarytool info ${UUID} ${credentials} OUTPUT_VARIABLE STATUS_OUTPUT) set(regex_status "status: ([a-zA-Z| ]+)") FETCH_REGEX(STATUS "${regex_status}" "${STATUS_OUTPUT}") @@ -49,7 +49,7 @@ message(STATUS "Notarization succeeded...") else() message(STATUS "Fetched Status: ${STATUS}") - execute_process(COMMAND ${XCRUN} notarytool log ${UUID} ${keychain}) + execute_process(COMMAND ${XCRUN} notarytool log ${UUID} ${credentials}) message(FATAL_ERROR "Notarization failed:\n${STATUS_OUTPUT}") endif() diff -Nru ausweisapp2-2.3.1/cmake/Packaging.android.cmake ausweisapp2-2.4.0/cmake/Packaging.android.cmake --- ausweisapp2-2.3.1/cmake/Packaging.android.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Packaging.android.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -143,6 +143,7 @@ set(DESTINATION_ANDROID_FILE_BASE ${PROJECT_BINARY_DIR}/dist/${CPACK_PACKAGE_FILE_NAME}) set(DESTINATION_ANDROID_FILE ${DESTINATION_ANDROID_FILE_BASE}.${ANDROID_FILE_EXT}) +set(DESTINATION_ANDROID_FILE_AAB ${DESTINATION_ANDROID_FILE_BASE}.aab) if(INTEGRATED_SDK) add_custom_command(TARGET aar POST_BUILD COMMAND ${ANDROID_BUILD_DIR}/gradlew sourcesJar @@ -171,11 +172,23 @@ WORKING_DIRECTORY ${ANDROID_BUILD_DIR}) else() find_program(apksigner apksigner HINTS ${ANDROID_SDK_ROOT}/build-tools/${ANDROID_BUILD_TOOLS_REVISION} CMAKE_FIND_ROOT_PATH_BOTH) + find_program(jarsigner jarsigner CMAKE_FIND_ROOT_PATH_BOTH) + + if(apksigner OR jarsigner) + add_custom_target(verify.signature) + endif() + if(apksigner) if(QT_ANDROID_SIGN_APK) set(APKSIGNER_PARAM -v4-signature-file ${DESTINATION_ANDROID_FILE}.idsig) endif() - add_custom_target(verify.signature COMMAND ${apksigner} verify --verbose --print-certs -Werr ${APKSIGNER_PARAM} ${DESTINATION_ANDROID_FILE}) + add_custom_target(verify.signature.apk COMMAND ${apksigner} verify --verbose --print-certs -Werr ${APKSIGNER_PARAM} ${DESTINATION_ANDROID_FILE}) + add_dependencies(verify.signature verify.signature.apk) + endif() + + if(jarsigner) + add_custom_target(verify.signature.aab COMMAND ${jarsigner} -verify -verbose -certs ${DESTINATION_ANDROID_FILE_AAB}) + add_dependencies(verify.signature verify.signature.aab) endif() find_program(aapt NAMES aapt2 aapt HINTS ${ANDROID_SDK_ROOT}/build-tools/${ANDROID_BUILD_TOOLS_REVISION} CMAKE_FIND_ROOT_PATH_BOTH) diff -Nru ausweisapp2-2.3.1/cmake/Packaging.cmake ausweisapp2-2.4.0/cmake/Packaging.cmake --- ausweisapp2-2.3.1/cmake/Packaging.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Packaging.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -43,6 +43,9 @@ set(CPACK_PACKAGE_CONTACT "support@ausweisapp.de") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "AusweisApp") set(CPACK_PACKAGE_FILE_NAME ${FILENAME}) +if(USE_DISTRIBUTION_PROFILE) + set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}-appstore) +endif() set(CPACK_PACKAGE_INSTALL_DIRECTORY ${PROJECT_NAME}) if(NOT CONTAINER_SDK) set(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.rst") @@ -58,15 +61,20 @@ find_program(ENSCRIPT enscript) find_program(GS gs) +set(LICENSE_FILE_DIST ${FILENAME}-Lizenz) +set(LICENSE_FILE_TXT ${LICENSE_FILE_DIST}.txt) +add_custom_command(OUTPUT ${LICENSE_FILE_TXT} COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CPACK_RESOURCE_FILE_LICENSE} ${LICENSE_FILE_TXT}) +add_custom_target(license DEPENDS ${LICENSE_FILE_TXT}) if(CONV AND ENSCRIPT AND GS) - set(LICENSE_FILE ${FILENAME}-Lizenz.pdf) + set(LICENSE_FILE_PDF ${LICENSE_FILE_DIST}.pdf) set(CONV_CMD ${CONV} -f utf-8 -t iso-8859-1 --output license.tmpfile.conv ${CPACK_RESOURCE_FILE_LICENSE}) set(ENSCRIPT_CMD ${ENSCRIPT} -q -X 88591 -B --word-wrap -t ${PROJECT_NAME} --output license.tmpfile.ps license.tmpfile.conv) - set(GS_CMD ${GS} -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=${LICENSE_FILE} license.tmpfile.ps) + set(GS_CMD ${GS} -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=${LICENSE_FILE_PDF} license.tmpfile.ps) - add_custom_command(OUTPUT ${LICENSE_FILE} COMMAND ${CONV_CMD} COMMAND ${ENSCRIPT_CMD} COMMAND ${GS_CMD}) - add_custom_target(license DEPENDS ${LICENSE_FILE}) + add_custom_command(OUTPUT ${LICENSE_FILE_PDF} COMMAND ${CONV_CMD} COMMAND ${ENSCRIPT_CMD} COMMAND ${GS_CMD}) + add_custom_target(license.pdf DEPENDS ${LICENSE_FILE_PDF}) + add_dependencies(license license.pdf) endif() if(APPLE AND NOT IOS) @@ -101,39 +109,60 @@ if(WIN32) - set(CPACK_GENERATOR WIX) - set(CPACK_WIX_UPGRADE_GUID 4EE0E467-EAB7-483E-AB45-87BD1DB6B037) - set(CPACK_WIX_PRODUCT_ICON ${RESOURCES_DIR}/images/desktop/npa.ico) - set(CPACK_WIX_CULTURES de-DE en-US) - # disable above line, enable beneath line to build MSI for english - # set(CPACK_WIX_CULTURES en-US) - set(CPACK_WIX_TEMPLATE ${PACKAGING_DIR}/win/WIX.template.in) - set(CPACK_WIX_EXTRA_SOURCES ${PACKAGING_DIR}/win/executable.wxs ${PACKAGING_DIR}/win/install_settings.wxs ${PACKAGING_DIR}/win/runtime_settings.wxs ${PACKAGING_DIR}/win/gui.wxs) - set(CPACK_WIX_UI_BANNER ${RESOURCES_DIR}/images/wix_banner.jpg) - set(CPACK_WIX_UI_DIALOG ${RESOURCES_DIR}/images/wix_dialog.jpg) - set(CPACK_WIX_EXTENSIONS WixUtilExtension WixFirewallExtension) - set(CPACK_WIX_LIGHT_EXTRA_FLAGS -loc ${PACKAGING_DIR}/win/WIX.Texts.de-DE.wxl -loc ${PACKAGING_DIR}/win/WIX.Texts.en-US.wxl) - # suppress errors related to the shortcuts (HKCU/HKLM) - set(CPACK_WIX_LIGHT_EXTRA_FLAGS -sice:ICE38 -sice:ICE43 -sice:ICE57 ${CPACK_WIX_LIGHT_EXTRA_FLAGS}) - - configure_file(${CMAKE_DIR}/PrepareProxy.cmake.in ${CMAKE_BINARY_DIR}/PrepareProxy.cmake @ONLY) - set(CPACK_PRE_BUILD_SCRIPTS "${CMAKE_BINARY_DIR}/PrepareProxy.cmake") - - if(SIGNTOOL_CMD) - message(STATUS "MSI can be signed with 'make package.sign'") - 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}) + set(WIX_TOOLSET_MIN_VERSION 6) + function(wixtoolset_validator validator_result binary) + execute_process(COMMAND ${binary} --version OUTPUT_VARIABLE WIX_TOOLSET_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) + + message(STATUS "WiX Toolset Version: ${WIX_TOOLSET_VERSION}") + if(WIX_TOOLSET_VERSION VERSION_LESS WIX_TOOLSET_MIN_VERSION) + message(STATUS "WiX Toolset Version ${WIX_TOOLSET_MIN_VERSION} is required to build target package.") + set(${validator_result} FALSE PARENT_SCOPE) + endif() + endfunction() + + find_program(WIX_TOOLSET wix VALIDATOR wixtoolset_validator CMAKE_FIND_ROOT_PATH_BOTH) + if(WIX_TOOLSET) + set(CPACK_GENERATOR WIX) + set(CPACK_WIX_VERSION 4) # Package using WiX .NET Tools (v4 and above) + set(CPACK_WIX_UPGRADE_GUID 4EE0E467-EAB7-483E-AB45-87BD1DB6B037) + set(CPACK_WIX_PRODUCT_ICON ${RESOURCES_DIR}/images/desktop/npa.ico) + set(CPACK_WIX_CULTURES de-DE en-US) + # disable above line, enable beneath line to build MSI for english + # set(CPACK_WIX_CULTURES en-US) + set(CPACK_WIX_TEMPLATE ${PACKAGING_DIR}/win/WIX.template.in) + set(CPACK_WIX_EXTRA_SOURCES ${PACKAGING_DIR}/win/executable.wxs ${PACKAGING_DIR}/win/install_settings.wxs ${PACKAGING_DIR}/win/runtime_settings.wxs ${PACKAGING_DIR}/win/gui.wxs) + set(CPACK_WIX_UI_BANNER ${RESOURCES_DIR}/images/wix_banner.jpg) + set(CPACK_WIX_UI_DIALOG ${RESOURCES_DIR}/images/wix_dialog.jpg) + set(CPACK_WIX_EXTENSIONS WixToolset.Util.wixext WixToolset.Firewall.wixext) + set(CPACK_WIX_BUILD_EXTRA_FLAGS -loc ${PACKAGING_DIR}/win/WIX.Texts.de-DE.wxl -loc ${PACKAGING_DIR}/win/WIX.Texts.en-US.wxl) + if(CMAKE_VERSION VERSION_LESS "4.1") + set(CPACK_WIX_BUILD_EXTRA_FLAGS -i ${CMAKE_BINARY_DIR}/_CPack_Packages/win64/WIX ${CPACK_WIX_BUILD_EXTRA_FLAGS}) + endif() + + configure_file(${CMAKE_DIR}/PrepareExecutable.cmake.in ${CMAKE_BINARY_DIR}/PrepareExecutable.cmake @ONLY) + set(CPACK_PRE_BUILD_SCRIPTS "${CMAKE_BINARY_DIR}/PrepareExecutable.cmake") + + if(SIGNTOOL_CMD) + message(STATUS "MSI can be signed with 'cmake --build . --target package.sign'") + 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() + + else() + unset(CPACK_GENERATOR) endif() elseif(IOS) configure_file("${CMAKE_DIR}/iOS.bundles.cmake.in" "${CMAKE_BINARY_DIR}/iOS.bundles.cmake" @ONLY) if(INTEGRATED_SDK) set(Bundle_Target zip) + list(APPEND Bundle_Suffix ${CMAKE_OSX_SYSROOT} ${CMAKE_OSX_ARCHITECTURES}) + list(JOIN Bundle_Suffix "_" Bundle_Suffix) else() set(Bundle_Target ipa) endif() - add_custom_target(${Bundle_Target} COMMAND ${CMAKE_COMMAND} -DINTEGRATED_SDK=${INTEGRATED_SDK} -DCONFIG=$ -DIOS=${IOS} -P ${CMAKE_BINARY_DIR}/iOS.bundles.cmake) + add_custom_target(${Bundle_Target} COMMAND ${CMAKE_COMMAND} -DINTEGRATED_SDK=${INTEGRATED_SDK} -DSUFFIX=${Bundle_Suffix} -DCONFIG=$ -DIOS=${IOS} -P ${CMAKE_BINARY_DIR}/iOS.bundles.cmake) elseif(APPLE) set(CPACK_GENERATOR External DragNDrop) @@ -159,5 +188,6 @@ set(CPACK_GENERATOR STGZ) endif() - -include(CPack) +if(NOT "${CPACK_GENERATOR}" STREQUAL "") + include(CPack) +endif() diff -Nru ausweisapp2-2.3.1/cmake/Pandoc.cmake ausweisapp2-2.4.0/cmake/Pandoc.cmake --- ausweisapp2-2.3.1/cmake/Pandoc.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Pandoc.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,101 @@ +function(PANDOC_RST_TO_PDF DOCS_DIR _target) + set(options) + set(oneValueArgs OUTPUT_NAME TRANSLATE) + set(multiValueArgs CUSTOM_PANDOC_PARAM) + cmake_parse_arguments(PANDOC_RST_TO_PDF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(CMAKE_PANDOC_DIR "${CMAKE_DIR}/pandoc") + set(PREPROCESS_SCRIPT "${CMAKE_PANDOC_DIR}/preprocess_rst.py") + + set(BUILD_OUTPUT_DIR "${CMAKE_BINARY_DIR}/docs/${_target}") + file(MAKE_DIRECTORY "${BUILD_OUTPUT_DIR}") + + # Configure metadata.yaml + if(PANDOC_RST_TO_PDF_TRANSLATE) + configure_file("${DOCS_DIR}/metadata_${PANDOC_RST_TO_PDF_TRANSLATE}.yaml.in" "${BUILD_OUTPUT_DIR}/metadata.yaml" @ONLY) + else() + configure_file("${DOCS_DIR}/metadata.yaml.in" "${BUILD_OUTPUT_DIR}/metadata.yaml" @ONLY) + endif() + + # Find lua filters + file(GLOB_RECURSE FILTERS "${CMAKE_PANDOC_DIR}/*.lua") + foreach(FILTER IN LISTS FILTERS) + list(APPEND CUSTOM_LUA_FILTERS "--lua-filter=${FILTER}") + endforeach() + + # Copy pregenerated PDFs + file(GLOB_RECURSE INPUT_PDF_FILES "${DOCS_DIR}/*.pdf") + foreach(INPUT_PDF IN LISTS INPUT_PDF_FILES) + get_filename_component(PDF_FILENAME "${INPUT_PDF}" NAME_WLE) + set(OUTPUT_PDF "${BUILD_OUTPUT_DIR}/${PDF_FILENAME}.pdf") + set(TARGET_NAME "docs_${_target}_${PDF_FILENAME}.pdf") + add_custom_command( + OUTPUT "${OUTPUT_PDF}" + COMMAND ${CMAKE_COMMAND} -E copy "${INPUT_PDF}" "${OUTPUT_PDF}" + DEPENDS "${INPUT_PDF}" + VERBATIM + ) + add_custom_target("${TARGET_NAME}" + DEPENDS "${OUTPUT_PDF}" + ) + list(APPEND _dep_targets "${TARGET_NAME}") + endforeach() + + if(PANDOC_RST_TO_PDF_TRANSLATE) + set(PYTHON_SCRIPT_TRANSLATE_ARGUMENT "--translate=${PANDOC_RST_TO_PDF_TRANSLATE}") + else() + set(PYTHON_SCRIPT_TRANSLATE_ARGUMENT "") + endif() + + # Preprocess index file + set(INPUT_RST "${DOCS_DIR}/index.rst") + set(OUTPUT_RST "${BUILD_OUTPUT_DIR}/index.rst") + add_custom_command( + OUTPUT "${OUTPUT_RST}" + COMMAND + "${Python_EXECUTABLE}" + "${PREPROCESS_SCRIPT}" + "${INPUT_RST}" + "${OUTPUT_RST}" + "${PROJECT_NAME}" + "${PROJECT_VERSION}" + ${PYTHON_SCRIPT_TRANSLATE_ARGUMENT} + DEPENDS + "${INPUT_RST}" + "${PREPROCESS_SCRIPT}" + WORKING_DIRECTORY "${BUILD_OUTPUT_DIR}" + VERBATIM + ) + add_custom_target("docs_${_target}_index_rst" DEPENDS "${OUTPUT_RST}") + list(APPEND _dep_targets "docs_${_target}_index_rst") + + # Collect rst files index.rst depends on + file(GLOB_RECURSE RST_FILES "${DOCS_DIR}/*.rst") + list(APPEND _dep_targets ${RST_FILES}) + + # Convert index to final pdf + set(TARGET_NAME "${_target}.latex.pdf") + set(OUTPUT_PATH "${BUILD_OUTPUT_DIR}/${PROJECT_NAME}-${VERSION_DVCS}-${PANDOC_RST_TO_PDF_OUTPUT_NAME}.pdf") + add_custom_command( + OUTPUT "${OUTPUT_PATH}" + COMMAND + ${PANDOC_BIN} + "--metadata-file=metadata.yaml" + "--template=${CMAKE_PANDOC_DIR}/template.tex" + ${CUSTOM_LUA_FILTERS} + ${PANDOC_RST_TO_PDF_CUSTOM_PANDOC_PARAM} + "${OUTPUT_RST}" + --to=pdf + "--output=${OUTPUT_PATH}" + DEPENDS + ${_dep_targets} + ${OUTPUT_RST} + ${FILTERS} + "${PREPROCESS_SCRIPT}" + "${BUILD_OUTPUT_DIR}/metadata.yaml" + "${CMAKE_PANDOC_DIR}/template.tex" + WORKING_DIRECTORY "${BUILD_OUTPUT_DIR}" + VERBATIM + ) + add_custom_target("${TARGET_NAME}" DEPENDS "${OUTPUT_PATH}") +endfunction() diff -Nru ausweisapp2-2.3.1/cmake/PrepareExecutable.cmake.in ausweisapp2-2.4.0/cmake/PrepareExecutable.cmake.in --- ausweisapp2-2.3.1/cmake/PrepareExecutable.cmake.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/PrepareExecutable.cmake.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,13 @@ +set(WIX_BUILD_DIR "@CMAKE_BINARY_DIR@/_CPack_Packages/win64/WIX") +file(GLOB WIX_INSTALL_DIR RELATIVE ${WIX_BUILD_DIR} ${WIX_BUILD_DIR}/@PROJECT_NAME@-*) + +list(LENGTH WIX_INSTALL_DIR DIR_COUNT) +if(NOT DIR_COUNT EQUAL 1 OR NOT IS_DIRECTORY "${WIX_BUILD_DIR}/${WIX_INSTALL_DIR}") + message(FATAL_ERROR "No unique CPack installation directory was found: ${WIX_INSTALL_DIR}") +endif() + +set(PROJECT_EXECUTABLE_NAME "@PROJECT_NAME@@CMAKE_EXECUTABLE_SUFFIX@") + +file(RENAME + "${WIX_BUILD_DIR}/${WIX_INSTALL_DIR}/${PROJECT_EXECUTABLE_NAME}" + "@CMAKE_BINARY_DIR@/${PROJECT_EXECUTABLE_NAME}") diff -Nru ausweisapp2-2.3.1/cmake/PrepareProxy.cmake.in ausweisapp2-2.4.0/cmake/PrepareProxy.cmake.in --- ausweisapp2-2.3.1/cmake/PrepareProxy.cmake.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/PrepareProxy.cmake.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -set(WIX_BUILD_DIR "@CMAKE_BINARY_DIR@/_CPack_Packages/win64/WIX") -file(GLOB WIX_INSTALL_DIR RELATIVE ${WIX_BUILD_DIR} ${WIX_BUILD_DIR}/@PROJECT_NAME@-*) - -list(LENGTH WIX_INSTALL_DIR DIR_COUNT) -if(NOT DIR_COUNT EQUAL 1 OR NOT IS_DIRECTORY "${WIX_BUILD_DIR}/${WIX_INSTALL_DIR}") - message(FATAL_ERROR "No unique CPack installation directory was found: ${WIX_INSTALL_DIR}") -endif() - -set(PROJECT_EXECUTABLE_NAME "@PROJECT_NAME@@CMAKE_EXECUTABLE_SUFFIX@") - -file(RENAME - "${WIX_BUILD_DIR}/${WIX_INSTALL_DIR}/${PROJECT_EXECUTABLE_NAME}" - "${WIX_BUILD_DIR}/${PROJECT_EXECUTABLE_NAME}") diff -Nru ausweisapp2-2.3.1/cmake/Provider.cmake ausweisapp2-2.4.0/cmake/Provider.cmake --- ausweisapp2-2.3.1/cmake/Provider.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Provider.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.25) + +macro(provide_dependency method name) + set(ARGS ${ARGN}) + + if("${name}" MATCHES "Qt" OR "${name}" MATCHES "OpenSSL" OR "${name}" MATCHES "Governikus") + set(PREFIX_PATH "${CMAKE_BINARY_DIR}/provider") + if(NOT EXISTS "${PREFIX_PATH}") + execute_process(COMMAND ${CMAKE_COMMAND} -DNAME=Provider -DPROVIDER=${PROVIDER} -P ${CMAKE_SOURCE_DIR}/ci.cmake + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + endif() + list(APPEND ARGS NO_DEFAULT_PATH PATHS ${PREFIX_PATH}) + set_directory_properties(PROPERTIES ADDITIONAL_CLEAN_FILES "${PREFIX_PATH}") + endif() + + find_package(${name} ${ARGS} BYPASS_PROVIDER) +endmacro() + +cmake_language( + SET_DEPENDENCY_PROVIDER provide_dependency + SUPPORTED_METHODS FIND_PACKAGE +) + +message(STATUS "Governikus dependency provider enabled!") diff -Nru ausweisapp2-2.3.1/cmake/SignFiles.cmake.in ausweisapp2-2.4.0/cmake/SignFiles.cmake.in --- ausweisapp2-2.3.1/cmake/SignFiles.cmake.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/SignFiles.cmake.in 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.19.0) +cmake_minimum_required(VERSION 3.25) if(APPLE AND NOT IOS) string(FIND "${CMAKE_BINARY_DIR}" "DragNDrop" IS_DMG) diff -Nru ausweisapp2-2.3.1/cmake/Sphinx.cmake ausweisapp2-2.4.0/cmake/Sphinx.cmake --- ausweisapp2-2.3.1/cmake/Sphinx.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Sphinx.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -41,7 +41,12 @@ list(APPEND target_list ${subtarget}) if("${_builder}" STREQUAL "latex") - add_custom_target(${subtarget}.pdf COMMAND make WORKING_DIRECTORY ${target_dir} DEPENDS ${subtarget}) + if(WIN32) + set(MAKE_BIN make.bat) + else() + find_program(MAKE_BIN NAMES gmake make) + endif() + add_custom_target(${subtarget}.pdf COMMAND ${MAKE_BIN} WORKING_DIRECTORY ${target_dir} DEPENDS ${subtarget}) list(APPEND target_list_pdf ${subtarget}.pdf) endif() endforeach() diff -Nru ausweisapp2-2.3.1/cmake/SwiftPackage.cmake ausweisapp2-2.4.0/cmake/SwiftPackage.cmake --- ausweisapp2-2.3.1/cmake/SwiftPackage.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/SwiftPackage.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.25) if(NOT CMAKE_SCRIPT_MODE_FILE) message(STATUS "Usage: cmake -P cmake/SwiftPackage.cmake") @@ -16,8 +16,7 @@ set(prefix AusweisApp2) -set(extension .framework.zip) -file(GLOB_RECURSE FRAMEWORK_ZIP RELATIVE "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}/*${extension}") +file(GLOB_RECURSE FRAMEWORK_ZIP "${CMAKE_BINARY_DIR}/*.framework.zip") if(NOT FRAMEWORK_ZIP) message(FATAL_ERROR "Missing framework(s) in: ${CMAKE_BINARY_DIR}") endif() @@ -26,12 +25,14 @@ foreach(framework ${FRAMEWORK_ZIP}) message(STATUS "Check framework: ${framework}") - get_filename_component(dir ${framework} DIRECTORY) - get_filename_component(filename ${framework} NAME) - # Check that any framework has same version - string(REPLACE "${extension}" "" framework_version "${filename}") - string(REPLACE "${prefix}-" "" framework_version "${framework_version}") + get_filename_component(framework_version ${framework} NAME) + get_filename_component(framework_version ${framework_version} NAME_WLE) # strip .zip + get_filename_component(framework_version ${framework_version} NAME_WLE) # strip .framework + get_filename_component(framework_arch ${framework_version} LAST_EXT) + get_filename_component(framework_version ${framework_version} NAME_WLE) # strip framework_arch + string(REPLACE "${prefix}-" "" framework_version "${framework_version}") # strip AusweisApp + if(APP_NAME_VERSION AND NOT APP_NAME_VERSION STREQUAL framework_version) message(STATUS "Different package version: ${APP_NAME_VERSION} / ${framework_version}") set(APP_MISMATCH -version-mismatch) @@ -40,10 +41,12 @@ endif() # Extract the .zip file to package the framework itself - set(framework_dir ${dir}/${prefix}.framework) - file(REMOVE_RECURSE ${framework_dir}) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${filename}" WORKING_DIRECTORY "${dir}") - if (NOT ${framework_dir} MATCHES simulator) + set(framework_arch_dir ${CMAKE_BINARY_DIR}/arch${framework_arch}) + set(framework_dir ${framework_arch_dir}/${prefix}.framework) + file(REMOVE_RECURSE ${framework_arch_dir}) + file(MAKE_DIRECTORY ${framework_arch_dir}) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${framework}" WORKING_DIRECTORY "${framework_arch_dir}") + if (NOT ${framework_arch} MATCHES simulator) list(APPEND FRAMEWORK_PARAM -framework ${framework_dir}) else() if (NOT FAT_LIB_TARGETS) @@ -54,15 +57,15 @@ endif() endforeach() -if (FAT_LIB_TARGETS) +if(FAT_LIB_TARGETS) execute_process(COMMAND ${LIPO} ${FAT_LIB_TARGETS} -output ${FAT_LIB_OUTPUT}) endif() file(REMOVE_RECURSE ${prefix}.xcframework) execute_process(COMMAND ${XCODEBUILD} -create-xcframework ${FRAMEWORK_PARAM} -output ${prefix}.xcframework) -file(REMOVE_RECURSE ${FRAMEWORK_PARAM}) get_filename_component(SCRIPT_DIR "${CMAKE_SCRIPT_MODE_FILE}" DIRECTORY) +configure_file("${SCRIPT_DIR}/../LICENSE.officially.txt" "${CMAKE_BINARY_DIR}/LICENSE.txt" COPYONLY) set(PACKAGING_DIR "${SCRIPT_DIR}/../resources/packaging") configure_file("${PACKAGING_DIR}/ios/Package.swift" "${CMAKE_BINARY_DIR}/Package.swift" COPYONLY) @@ -78,4 +81,4 @@ set(dist_filename ${DIST_DIR}/${prefix}-${APP_NAME_VERSION}${APP_MISMATCH}.swiftpackage.zip) message(STATUS "Create package: ${dist_filename}") -execute_process(COMMAND ${CMAKE_COMMAND} -E tar cf "${dist_filename}" --format=zip ${prefix}.xcframework Package.swift) +execute_process(COMMAND ${CMAKE_COMMAND} -E tar cf "${dist_filename}" --format=zip LICENSE.txt ${prefix}.xcframework Package.swift) diff -Nru ausweisapp2-2.3.1/cmake/Tools.Libraries.cmake ausweisapp2-2.4.0/cmake/Tools.Libraries.cmake --- ausweisapp2-2.3.1/cmake/Tools.Libraries.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Tools.Libraries.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -13,11 +13,7 @@ endif() endfunction() - if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.25") - set(VALIDATOR VALIDATOR qmlformat_validator) - endif() - - find_program(QMLFORMAT qmlformat HINTS "${QT_INSTALL_ARCHDATA}/bin" ${VALIDATOR} CMAKE_FIND_ROOT_PATH_BOTH) + find_program(QMLFORMAT qmlformat HINTS "${QT_INSTALL_ARCHDATA}/bin" VALIDATOR qmlformat_validator CMAKE_FIND_ROOT_PATH_BOTH) if(QMLFORMAT) execute_process(COMMAND ${QMLFORMAT} --version OUTPUT_VARIABLE QMLFORMAT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) string(REPLACE "qmlformat " "" QMLFORMAT_VERSION "${QMLFORMAT_VERSION}") diff -Nru ausweisapp2-2.3.1/cmake/Tools.cmake ausweisapp2-2.4.0/cmake/Tools.cmake --- ausweisapp2-2.3.1/cmake/Tools.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Tools.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -38,36 +38,6 @@ add_custom_target(cppcheck.report DEPENDS ${XML_FILE} ${XML_FILE_TESTS}) endif() -# CppNcss (http://cppncss.sourceforge.net) -find_program(CPPNCSS_BIN cppncss CMAKE_FIND_ROOT_PATH_BOTH) -if(CPPNCSS_BIN) - set(XML_FILE "${PROJECT_BINARY_DIR}/cppncss.xml") - set(CPPNCSS_CMD ${CPPNCSS_BIN} -k -r -p="${PROJECT_SOURCE_DIR}/" ${SRC_DIR} ${TEST_DIR}) - - add_custom_command(OUTPUT ${XML_FILE} COMMAND ${CPPNCSS_CMD} -x -f="${XML_FILE}") - add_custom_target(cppncss COMMAND ${CPPNCSS_CMD} -m=CCN,NCSS,function) - add_custom_target(cppncss.report DEPENDS ${XML_FILE}) -endif() - -# pmccabe (http://parisc-linux.org/~bame/pmccabe/) -find_program(PMCCABE_BIN pmccabe CMAKE_FIND_ROOT_PATH_BOTH) -if(PMCCABE_BIN) - add_custom_target(pmccabe COMMAND ${PMCCABE_BIN} -v ${SRC_DIR}/*.cpp ${TEST_DIR}/*.cpp) -endif() - -# Doxygen (http://www.doxygen.org) -# http://www.stack.nl/~dimitri/doxygen/manual/config.html -find_package(Doxygen) -if(DOXYGEN_FOUND) - set(DOXYGEN_BIN_DIR "${PROJECT_BINARY_DIR}/doxygen") - set(DOXYGEN_CMD ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxyfile) - set(DOXYGEN_CFG ${PROJECT_SOURCE_DIR}/Doxyfile.in) - configure_file(${DOXYGEN_CFG} ${PROJECT_BINARY_DIR}/Doxyfile @ONLY) - - add_custom_command(OUTPUT ${DOXYGEN_BIN_DIR} COMMAND ${DOXYGEN_CMD} USES_TERMINAL) - add_custom_target(doxy DEPENDS ${DOXYGEN_BIN_DIR} WORKING_DIRECTORY ${PROJECT_BINARY_DIR} SOURCES ${DOXYGEN_CFG}) -endif() - find_program(CLOC_BIN cloc CMAKE_FIND_ROOT_PATH_BOTH) if(CLOC_BIN) set(CLOC_FILE "${PROJECT_BINARY_DIR}/cloc.xml") @@ -91,13 +61,13 @@ 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}") - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.java) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.cpp) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.h) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.h.in) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.mm) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.m) - file(GLOB_RECURSE FILES ${GLOB_FILES}) + list(APPEND GLOB_UNCRUSTIFY ${PROJECT_SOURCE_DIR}/*.java) + list(APPEND GLOB_UNCRUSTIFY ${PROJECT_SOURCE_DIR}/*.cpp) + list(APPEND GLOB_UNCRUSTIFY ${PROJECT_SOURCE_DIR}/*.h) + list(APPEND GLOB_UNCRUSTIFY ${PROJECT_SOURCE_DIR}/*.h.in) + list(APPEND GLOB_UNCRUSTIFY ${PROJECT_SOURCE_DIR}/*.mm) + list(APPEND GLOB_UNCRUSTIFY ${PROJECT_SOURCE_DIR}/*.m) + file(GLOB_RECURSE FILES ${GLOB_UNCRUSTIFY}) set(FORMATTING_FILE ${PROJECT_BINARY_DIR}/formatting.files) file(WRITE ${FORMATTING_FILE} "") @@ -109,7 +79,7 @@ endforeach() set(UNCRUSTIFY_CFG ${PROJECT_SOURCE_DIR}/uncrustify.cfg) - set(UNCRUSTIFY_CMD ${UNCRUSTIFY} -c ${UNCRUSTIFY_CFG} --replace --no-backup -q -F ${FORMATTING_FILE}) + set(UNCRUSTIFY_CMD ${UNCRUSTIFY} -c ${UNCRUSTIFY_CFG} --replace --no-backup -L1,2,4 -s -F ${FORMATTING_FILE}) add_custom_target(format.uncrustify COMMAND ${UNCRUSTIFY_CMD} SOURCES ${UNCRUSTIFY_CFG} ${FILES}) add_dependencies(format format.uncrustify) endif() @@ -117,23 +87,38 @@ find_program(DOS2UNIX dos2unix CMAKE_FIND_ROOT_PATH_BOTH) if(DOS2UNIX) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.cpp) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.h) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.rst) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.svg) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/CMakeLists.txt) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.cmake) - list(APPEND GLOB_FILES ${PROJECT_SOURCE_DIR}/*.cmake.in) - file(GLOB_RECURSE FILES ${GLOB_FILES}) + list(APPEND GLOB_DOS2UNIX ${PROJECT_SOURCE_DIR}/*.cpp) + list(APPEND GLOB_DOS2UNIX ${PROJECT_SOURCE_DIR}/*.h) + list(APPEND GLOB_DOS2UNIX ${PROJECT_SOURCE_DIR}/*.rst) + list(APPEND GLOB_DOS2UNIX ${PROJECT_SOURCE_DIR}/*.svg) + list(APPEND GLOB_DOS2UNIX ${PROJECT_SOURCE_DIR}/CMakeLists.txt) + list(APPEND GLOB_DOS2UNIX ${PROJECT_SOURCE_DIR}/*.cmake) + list(APPEND GLOB_DOS2UNIX ${PROJECT_SOURCE_DIR}/*.cmake.in) + file(GLOB_RECURSE FILES ${GLOB_DOS2UNIX}) foreach(FILE ${FILES}) - list(APPEND commands COMMAND ${DOS2UNIX} -q -k --add-eol ${FILE}) + list(APPEND commands_dos2unix COMMAND ${DOS2UNIX} -q -k --add-eol ${FILE}) endforeach() - add_custom_target(format.dos2unix ${commands}) + add_custom_target(format.dos2unix ${commands_dos2unix}) add_dependencies(format format.dos2unix) endif() +find_program(WIX wix CMAKE_FIND_ROOT_PATH_BOTH) +if(WIX AND WIN32) + list(APPEND GLOB_WIX ${PROJECT_SOURCE_DIR}/*.wxs) + list(APPEND GLOB_WIX ${PROJECT_SOURCE_DIR}/*.wxl) + list(APPEND GLOB_WIX ${PROJECT_SOURCE_DIR}/WIX.template.in) + file(GLOB_RECURSE FILES ${GLOB_WIX}) + + foreach(FILE ${FILES}) + list(APPEND commands_wix COMMAND ${WIX} format ${FILE}) + endforeach() + + add_custom_target(format.wix ${commands_wix}) + add_dependencies(format format.wix) +endif() + find_package(Python) if(Python_FOUND) list(APPEND GLOB_JSON ${RESOURCES_DIR}/updatable-files/*.json) @@ -141,11 +126,11 @@ file(GLOB_RECURSE JSON_FILES ${GLOB_JSON}) foreach(JSON_FILE ${JSON_FILES}) - list(APPEND commands + list(APPEND commands_json COMMAND ${Python_EXECUTABLE} -m json.tool --no-ensure-ascii --tab ${JSON_FILE} ${JSON_FILE}) endforeach() - add_custom_target(format.json ${commands}) + add_custom_target(format.json ${commands_json}) add_dependencies(format format.json) if(EXISTS "${CMAKE_SOURCE_DIR}/utils") @@ -158,6 +143,26 @@ endif() endif() +find_program(RUFF ruff CMAKE_FIND_ROOT_PATH_BOTH) +if(RUFF) + list(APPEND GLOB_PY ${PROJECT_SOURCE_DIR}/*.py) + file(GLOB_RECURSE PY_FILES ${GLOB_PY}) + set(RUFF_CMD ${RUFF} format ${CMAKE_SOURCE_DIR}) + add_custom_target(format.ruff COMMAND ${RUFF_CMD} SOURCES ${PROJECT_SOURCE_DIR}/ruff.toml ${PY_FILES}) + add_dependencies(format format.ruff) +endif() + +find_program(YAMLFMT yamlfmt CMAKE_FIND_ROOT_PATH_BOTH) +if(YAMLFMT) + list(APPEND GLOB_YML ${PROJECT_SOURCE_DIR}/*.yml) + list(APPEND GLOB_YML ${PROJECT_SOURCE_DIR}/*.yaml) + file(GLOB_RECURSE YML_FILES ${GLOB_YML}) + list(APPEND YML_FILES ${PROJECT_SOURCE_DIR}/.yamllint) + + add_custom_target(format.yamlfmt COMMAND ${YAMLFMT} ${YML_FILES} SOURCES ${YML_FILES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) + add_dependencies(format format.yamlfmt) +endif() + # doc8 (https://pypi.python.org/pypi/doc8) find_program(DOC8_BIN doc8 CMAKE_FIND_ROOT_PATH_BOTH) function(CREATE_DOC8_TARGET _dir _name) @@ -598,5 +603,11 @@ WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) endif() +find_program(PANDOC_BIN NAMES pandoc CMAKE_FIND_ROOT_PATH_BOTH) +find_package(Python) +if(PANDOC_BIN AND Python_FOUND) + include(Pandoc) +endif() + include(Sphinx) include(Tools.Libraries) diff -Nru ausweisapp2-2.3.1/cmake/Translation.cmake.in ausweisapp2-2.4.0/cmake/Translation.cmake.in --- ausweisapp2-2.3.1/cmake/Translation.cmake.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Translation.cmake.in 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10.0) +cmake_minimum_required(VERSION 3.25) if (@UPDATE_TRANSLATIONS_ADD_DVCS@) foreach(file @TRANSLATION_FILES@) @@ -7,13 +7,13 @@ string(REPLACE "\">\n" "\ \">\n\ \n\ - DvcsAttributes\n\ - \n\ + DvcsAttributes\n\ + \n\ @dvcs_revision@\n\ revision\n\ @dvcs_revision@\n\ - \n\ - \n\ + \n\ + \n\ @PROJECT_VERSION@\n\ version\n\ @PROJECT_VERSION@\n\ diff -Nru ausweisapp2-2.3.1/cmake/Version.cmake ausweisapp2-2.4.0/cmake/Version.cmake --- ausweisapp2-2.3.1/cmake/Version.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/Version.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1 +1 @@ -set(VERSION 2.3.1) +set(VERSION 2.4.0) diff -Nru ausweisapp2-2.3.1/cmake/android.toolchain.cmake ausweisapp2-2.4.0/cmake/android.toolchain.cmake --- ausweisapp2-2.3.1/cmake/android.toolchain.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/android.toolchain.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -60,7 +60,11 @@ if(QT_TOOLCHAIN_FILE) message(STATUS "QT_TOOLCHAIN_FILE: ${QT_TOOLCHAIN_FILE}") + set(ARCH ${CMAKE_ANDROID_ARCH_ABI}) include(${QT_TOOLCHAIN_FILE}) + if(ARCH AND NOT ARCH STREQUAL CMAKE_ANDROID_ARCH_ABI) + message(FATAL_ERROR "Requested architecture (${ARCH}) not compatible with Qt architecture (${CMAKE_ANDROID_ARCH_ABI})") + endif() endif() set(ANDROID_BUILD_NAME android-build) @@ -68,11 +72,6 @@ set(ANDROID_PACKAGE_SRC_DIR ${PROJECT_BINARY_DIR}/package-src-dir) set(QT_ENABLE_VERBOSE_DEPLOYMENT ON) -if(DEFINED ENV{QT_ANDROID_KEYSTORE_PATH} AND NOT INTEGRATED_SDK) - set(QT_ANDROID_SIGN_APK ON) - set(QT_ANDROID_SIGN_AAB ON) -endif() - set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang) set(CMAKE_SYSTEM_NAME Android) set(CMAKE_SYSTEM_VERSION 28) diff -Nru ausweisapp2-2.3.1/cmake/ci/Android.cmake ausweisapp2-2.4.0/cmake/ci/Android.cmake --- ausweisapp2-2.3.1/cmake/ci/Android.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/Android.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -if(NAME MATCHES "_APK_") - set(APK ON) - set(PRESET ci-android-apk) -elseif(NAME MATCHES "_AAR_") - set(AAR ON) - set(PRESET ci-android-aar) -else() - step(${CMAKE_COMMAND} -DDIST_DIR=${T_DIST_DIR} -P ${CMAKE_DIR}/Merge.cmake) - - if(RELEASE) - set(files *-sources.jar *.aar *.pom) - foreach(file ${files}) - file(GLOB file "${T_DIST_DIR}/${file}") - step(gpg --batch --passphrase $ENV{GPG_PSW} --pinentry-mode loopback -a --detach-sig -u $ENV{GPG_ID} ${file}) - endforeach() - endif() - - if(NOT REVIEW) - step(${CMAKE_COMMAND} -DCMD=DEPLOY_NEXUS -DPUBLISH=$ENV{PUBLISH} -P ${CMAKE_DIR}/cmd.cmake CHDIR ${T_DIST_DIR}) - endif() - return() -endif() - -if(REVIEW) - set(PRESET ${PRESET}-review) -endif() - -step(${T_CFG} --preset ${PRESET}) -step(${T_BUILD}) - -if(APK) - step(${T_TARGET} apk) - step(${T_TARGET} verify.signature) - step(${T_TARGET} dump.apk) - if(RELEASE) - step(${T_TARGET} aab ENV - QT_ANDROID_KEYSTORE_PATH=$ENV{APK_SIGN_KEYSTORE_UPLOAD} - QT_ANDROID_KEYSTORE_ALIAS=$ENV{APK_SIGN_KEYSTORE_ALIAS_UPLOAD} - QT_ANDROID_KEYSTORE_STORE_PASS=$ENV{APK_SIGN_KEYSTORE_PSW_UPLOAD}) - else() - step(${T_TARGET} aab) - endif() - file(GLOB FILES "${T_DIST_DIR}/*.apk" "${T_DIST_DIR}/*.aab") -elseif(AAR) - step(${T_TARGET} aar) - file(GLOB FILES "${T_DIST_DIR}/*.aar") -endif() - -hashsum(${FILES}) - -if(NOT RELEASE) - step(${T_CTEST}) -endif() diff -Nru ausweisapp2-2.3.1/cmake/ci/Configuration.cmake ausweisapp2-2.4.0/cmake/ci/Configuration.cmake --- ausweisapp2-2.3.1/cmake/ci/Configuration.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/Configuration.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -step(${T_CFG} --preset ci-linux) -step(${T_TARGET} ALL_Test_configuration) -step(${T_CTEST} -R Test_configuration ENV QT_PLUGIN_PATH=${WORKSPACE}/libs/dist/plugins) -step(${T_CTEST} -L json) diff -Nru ausweisapp2-2.3.1/cmake/ci/Container.cmake ausweisapp2-2.4.0/cmake/ci/Container.cmake --- ausweisapp2-2.3.1/cmake/ci/Container.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/Container.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -if(NAME MATCHES "VNC") - set(VNC ON) - set(TARGET vnc) - set(Dockerfile -f ${CMAKE_SOURCE_DIR}/resources/jenkins/docker/Dockerfile) -else() - set(TARGET sdk) -endif() - -if(RELEASE) - set(TAG $ENV{changeset}) - set(FILE_TAG ${TAG}) -elseif(REVIEW) - set(TAG $ENV{BUILD_TAG}) - set(FILE_TAG ${TAG}) -else() - set(TAG dev-$ENV{MERCURIAL_REVISION_BRANCH}) - string(REPLACE "-default" "" TAG "${TAG}") - set(FILE_TAG $ENV{MERCURIAL_REVISION_SHORT}) -endif() - -if(NOT EXISTS ${T_BUILD_DIR}) - step(${CMAKE_COMMAND} -E make_directory ${T_BUILD_DIR}) -endif() - -if(NOT VNC) - step(docker container prune -f) -endif() - -step(docker build --pull - -t dev-docker.govkg.de/ausweisapp/${TARGET}:${TAG} - --build-arg CCACHE_REMOTE_STORAGE=redis://$ENV{CCACHE_REMOTE_STORAGE_HOST} - ${Dockerfile} - ${CMAKE_SOURCE_DIR} -) - -step(docker run --rm dev-docker.govkg.de/ausweisapp/${TARGET}:${TAG} AusweisApp --help) - -step(docker save -o ${T_BUILD_DIR}/AusweisApp-${FILE_TAG}.tar dev-docker.govkg.de/ausweisapp/${TARGET}:${TAG}) - -if(REVIEW) - step(docker rmi -f dev-docker.govkg.de/ausweisapp/${TARGET}:${TAG}) -else() - step(docker push dev-docker.govkg.de/ausweisapp/${TARGET}:${TAG}) -endif() - - -if(DAILY AND NOT VNC) - step(docker images --filter "dangling=true" -q OUTPUT IMAGES) - if(IMAGES) - string(STRIP "${IMAGES}" IMAGES) - string(REPLACE "\n" ";" IMAGES "${IMAGES}") - list(REVERSE IMAGES) - list(SUBLIST IMAGES 0 50 IMAGES) - foreach(entry ${IMAGES}) - step(docker rmi -f ${entry}) - endforeach() - endif() -endif() - - -if(RELEASE AND DEFINED ENV{LATEST}) - if($ENV{LATEST}) - step(docker tag dev-docker.govkg.de/ausweisapp/${TARGET}:${TAG} dev-docker.govkg.de/ausweisapp/${TARGET}:latest) - step(docker push dev-docker.govkg.de/ausweisapp/${TARGET}:latest) - endif() -endif() diff -Nru ausweisapp2-2.3.1/cmake/ci/Docker.cmake ausweisapp2-2.4.0/cmake/ci/Docker.cmake --- ausweisapp2-2.3.1/cmake/ci/Docker.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/Docker.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -include(Container) diff -Nru ausweisapp2-2.3.1/cmake/ci/Docs.cmake ausweisapp2-2.4.0/cmake/ci/Docs.cmake --- ausweisapp2-2.3.1/cmake/ci/Docs.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/Docs.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -step(${T_CFG} --preset ci-tools) - -step(${T_TARGET} notes) -step(${T_TARGET} notes.latex.pdf) -step(${CMAKE_COMMAND} -E tar cfJ ../AusweisApp_ReleaseNotes.tar.xz . CHDIR ${T_BUILD_DIR}/docs/notes) - -step(${T_TARGET} sdk) -step(${T_TARGET} sdk.latex.pdf) -step(${CMAKE_COMMAND} -E tar cfJ ../AusweisApp_SDK.tar.xz . CHDIR ${T_BUILD_DIR}/docs/sdk/html) - -step(${T_TARGET} failurecodes) -step(${T_TARGET} failurecodes.latex.pdf) - -step(${T_TARGET} installation_integration.latex.pdf) - -step(${T_TARGET} license) - -if(NOT RELEASE) - step(${T_TARGET} doc8) -endif() diff -Nru ausweisapp2-2.3.1/cmake/ci/Formatting.cmake ausweisapp2-2.4.0/cmake/ci/Formatting.cmake --- ausweisapp2-2.3.1/cmake/ci/Formatting.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/Formatting.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -step(${T_CFG} --preset ci-tools-with-libs) -step(${CMAKE_SOURCE_DIR}/resources/jenkins/check_formatting.sh ${PENDING_PATCH} CHDIR ${CMAKE_SOURCE_DIR}) -step(${CMAKE_COMMAND} -DCMD=CHECK_FAILURE_CODES -P ${CMAKE_DIR}/cmd.cmake CHDIR ${CMAKE_SOURCE_DIR}) diff -Nru ausweisapp2-2.3.1/cmake/ci/FreeBSD.cmake ausweisapp2-2.4.0/cmake/ci/FreeBSD.cmake --- ausweisapp2-2.3.1/cmake/ci/FreeBSD.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/FreeBSD.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -set(LD_ENV LD_LIBRARY_PATH=${WORKSPACE}/libs/dist/lib) - -step(${T_CFG} --preset ci-bsd) -step(${T_BUILD} ENV ${LD_ENV}) -step(${T_CTEST} ENV ${LD_ENV} QT_PLUGIN_PATH=${WORKSPACE}/libs/dist/plugins QML2_IMPORT_PATH=${WORKSPACE}/libs/dist/qml) diff -Nru ausweisapp2-2.3.1/cmake/ci/Linux.cmake ausweisapp2-2.4.0/cmake/ci/Linux.cmake --- ausweisapp2-2.3.1/cmake/ci/Linux.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/Linux.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -if(NAME MATCHES "Integrated") - set(INTEGRATED ON) -endif() - -if(INTEGRATED) - set(PRESET ci-integrated) -else() - set(PRESET ci-linux) -endif() - -step(${T_CFG} --preset ${PRESET}) -step(${T_BUILD}) -step(${T_CTEST} ENV QT_PLUGIN_PATH=${WORKSPACE}/libs/dist/plugins QML2_IMPORT_PATH=${WORKSPACE}/libs/dist/qml) -step(${CMAKE_COMMAND} --install ${T_BUILD_DIR} ENV DESTDIR=${WORKSPACE}/install) - -if(DAILY AND NOT INTEGRATED) - step(${T_TARGET} gcovr) - step(${T_TARGET} cloc.report) -endif() diff -Nru ausweisapp2-2.3.1/cmake/ci/MacOS.cmake ausweisapp2-2.4.0/cmake/ci/MacOS.cmake --- ausweisapp2-2.3.1/cmake/ci/MacOS.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/MacOS.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -if(NAME MATCHES "Integrated") - set(INTEGRATED ON) -endif() - -if(NAME MATCHES "DMG_PKG" OR RELEASE) - set(PACKAGES ON) -endif() - -if(PACKAGES) - set(PRESET ci-macos-release) -elseif(INTEGRATED) - set(PRESET ci-macos-integrated) -else() - set(PRESET ci-macos) - set(CTEST_CFG -C Debug) -endif() - -step(security unlock-keychain $ENV{KEYCHAIN_CREDENTIALS} $ENV{HOME}/Library/Keychains/login.keychain-db) - -step(${T_CFG} --preset ${PRESET}) - -if(PACKAGES) - step(${T_TARGET} package --config MinSizeRel) - step(${CMAKE_COMMAND} -E tar cf ../../AusweisApp.app.dSYM.zip --format=zip AusweisApp.app.dSYM CHDIR ${T_BUILD_DIR}/src/MinSizeRel) - - file(GLOB_RECURSE apps LIST_DIRECTORIES ON "${T_BUILD_DIR}/_CPack_Packages/Darwin") - list(FILTER apps INCLUDE REGEX "\\.app$") - set(dragndrop ${apps}) - list(FILTER dragndrop INCLUDE REGEX "/DragNDrop/") - if(NOT dragndrop OR NOT apps) - message(FATAL_ERROR "no *.app directory found") - endif() - foreach(app ${apps}) - step(codesign -vvvv ${app}) - endforeach() - foreach(app ${dragndrop}) - step(spctl -a -vv ${app}) - endforeach() - - if(NOT REVIEW) - step(${CMAKE_COMMAND} -P ${CMAKE_DIR}/Notarization.cmake CHDIR ${T_BUILD_DIR}) - endif() - - file(GLOB FILES "${T_BUILD_DIR}/*.dmg") - hashsum(${FILES}) -else() - step(${T_BUILD}) - step(${T_CTEST} ${CTEST_CFG} ENV QT_PLUGIN_PATH=${WORKSPACE}/libs/dist/plugins QML2_IMPORT_PATH=${WORKSPACE}/libs/dist/qml) -endif() - -if(RELEASE AND DEFINED ENV{USE_DISTRIBUTION_PROFILE}) - if($ENV{USE_DISTRIBUTION_PROFILE}) - file(GLOB pkgfile "${T_BUILD_DIR}/*.pkg") - step(xcrun altool -t osx --upload-app -u "ausweisapp@governikus.com" -p @env:PASSWORD -f ${pkgfile}) - endif() -endif() diff -Nru ausweisapp2-2.3.1/cmake/ci/SonarQube.cmake ausweisapp2-2.4.0/cmake/ci/SonarQube.cmake --- ausweisapp2-2.3.1/cmake/ci/SonarQube.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/SonarQube.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -set(CACHE_DIR cache) -if(NOT EXISTS ${CACHE_DIR}) - step(${CMAKE_COMMAND} -E make_directory ${CACHE_DIR}) -endif() - -set(SONARPATH ${WORKSPACE}/sonarqubetools) - -step(${CMAKE_COMMAND} -P ${CMAKE_DIR}/prepare_sonarqube_env.cmake) - -step(${T_CFG} --preset ci-linux) - -step(${SONARPATH}/dependency-check/bin/dependency-check.sh - --enableExperimental -f HTML -f JSON --scan ${CMAKE_DIR} --noupdate - --connectionString=jdbc:mariadb://dependency-check-db.govkg.de/dependencycheck - --dbUser=$ENV{DEPENDENCY_CHECK_USER} - --dbPassword=$ENV{DEPENDENCY_CHECK_PASSWORD} - --dbDriverName=org.mariadb.jdbc.Driver - CHDIR ${T_BUILD_DIR} -) - -step(${SONARPATH}/sonar-build-wrapper/build-wrapper-linux-x86-64 --out-dir ${T_BUILD_DIR} ${T_BUILD}) - -step(${T_CTEST} -LE qml -E Test_ui_qml_Qml) - -step(${T_TARGET} gcovr.sonar) - - - -if(REVIEW) - set(SONAR_CMDLINE - -Dsonar.pullrequest.key=$ENV{REVIEWBOARD_REVIEW_ID} - -Dsonar.pullrequest.branch=$ENV{MERCURIAL_REVISION_BRANCH} - -Dsonar.pullrequest.base=$ENV{MERCURIAL_REVISION_BRANCH} - ) -else() - set(SONAR_CMDLINE - -Dsonar.branch.name=$ENV{MERCURIAL_REVISION_BRANCH} - ) -endif() - -step( - ${SONARPATH}/sonar-scanner/bin/sonar-scanner - -Dsonar.scanner.metadataFilePath=${WORKSPACE}/tmp/sonar-metadata.txt - ${SONAR_CMDLINE} - -Dsonar.token=$ENV{SONARQUBE_TOKEN} - -Dsonar.qualitygate.wait=true - -Dsonar.qualitygate.timeout=90 - CHDIR ${T_BUILD_DIR} -) diff -Nru ausweisapp2-2.3.1/cmake/ci/Source.cmake ausweisapp2-2.4.0/cmake/ci/Source.cmake --- ausweisapp2-2.3.1/cmake/ci/Source.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/Source.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -step(${T_CFG} --preset ci-tools) -step(${T_TARGET} package_source) - -file(GLOB TARBALL "${T_BUILD_DIR}/*.tar.gz") -hashsum(${TARBALL}) - -if(RELEASE) - step(gpg --batch --passphrase $ENV{GPG_PSW} --pinentry-mode loopback -a --detach-sig -u $ENV{GPG_ID} ${TARBALL}) -endif() diff -Nru ausweisapp2-2.3.1/cmake/ci/Win.cmake ausweisapp2-2.4.0/cmake/ci/Win.cmake --- ausweisapp2-2.3.1/cmake/ci/Win.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/Win.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -if(NAME MATCHES "MSI") - set(PACKAGES ON) -endif() - -if(NAME MATCHES "GNU") - set(GNU ON) -elseif(NAME MATCHES "clang" AND DEFINED ENV{LLVM}) - set(PATH "$ENV{LLVM}") -endif() - -if(NOT GNU) - set(VCVARS cmd /c vcvarsall.bat amd64 && call) -endif() - -if(NAME MATCHES "_dev") - set(PRESET ci-win-debug) -elseif(PACKAGES) - set(PRESET ci-win-release) -else() - set(PRESET ci-win) -endif() - -step(${VCVARS} ${T_CFG} --preset ${PRESET} PATH "${PATH}") - -if(PACKAGES) - step(${VCVARS} ${T_TARGET} package) - step(${VCVARS} ${T_TARGET} package.sign) - file(GLOB FILES "${T_BUILD_DIR}/*.msi") - hashsum(${FILES}) - if(NOT RELEASE) - step(${CMAKE_COMMAND} -DCMD=CHECK_WIX_WARNING -DFILE=${T_BUILD_DIR}/_CPack_Packages/win64/WIX/wix.log -P ${CMAKE_DIR}/cmd.cmake) - endif() -else() - step(${VCVARS} ${T_BUILD}) -endif() - -if(NOT RELEASE AND NOT PACKAGES) - step(${T_CTEST} ENV QT_PLUGIN_PATH=${WORKSPACE}/libs/dist/plugins QML2_IMPORT_PATH=${WORKSPACE}/libs/dist/qml PATH "${WORKSPACE}/libs/dist/bin") -endif() diff -Nru ausweisapp2-2.3.1/cmake/ci/iOS.cmake ausweisapp2-2.4.0/cmake/ci/iOS.cmake --- ausweisapp2-2.3.1/cmake/ci/iOS.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/ci/iOS.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -if(NAME MATCHES "IPA") - set(IPA ON) - set(TARGET ipa) - set(PRESET ci-ios) -elseif(NAME MATCHES "Framework") - set(TARGET zip) - - if(NAME MATCHES "Framework_Simulator_arm64") - set(PRESET ci-ios-framework-simulator-arm64) - elseif(NAME MATCHES "Framework_Simulator") - set(PRESET ci-ios-framework-simulator) - else() - set(PRESET ci-ios-framework) - endif() -elseif(NAME MATCHES "SwiftPackage") - step(${CMAKE_COMMAND} -DDIST_DIR=${T_DIST_DIR} -P ${CMAKE_DIR}/SwiftPackage.cmake) - return() -endif() - -if(NAME MATCHES "Simulator") - set(PLATFORM "iOS Simulator") -else() - set(PLATFORM "iOS") -endif() - -step(security unlock-keychain $ENV{KEYCHAIN_CREDENTIALS} $ENV{HOME}/Library/Keychains/login.keychain-db) - -step(${T_CFG} --preset ${PRESET}) - -if(IPA AND NOT REVIEW) - step(xcodebuild -configuration MinSizeRel -archivePath AusweisApp.xcarchive -scheme AusweisAppBinary -destination "generic/platform=${PLATFORM}" archive CHDIR ${T_BUILD_DIR}) - step(xcodebuild -configuration MinSizeRel -archivePath AusweisApp.xcarchive -exportArchive -exportOptionsPlist exportOptions.plist -exportPath . CHDIR ${T_BUILD_DIR}) -else() - step(xcodebuild -configuration MinSizeRel -scheme AusweisAppBinary -destination "generic/platform=${PLATFORM}" CHDIR ${T_BUILD_DIR}) -endif() - -if(RELEASE) - step(${CMAKE_COMMAND} -E tar cf AusweisApp_BuildDir.tar.zstd --zstd build) -endif() - -step(xcodebuild -configuration MinSizeRel -target ${TARGET} CHDIR ${T_BUILD_DIR}) - -if(IPA AND NOT RELEASE) - step(${T_CTEST} -C MinSizeRel) -endif() - -if(IPA AND RELEASE AND DEFINED ENV{USE_DISTRIBUTION_PROFILE}) - if($ENV{USE_DISTRIBUTION_PROFILE}) - set(USER "ausweisapp@governikus.com") - file(GLOB ipafile "${T_BUILD_DIR}/*.ipa") - step(xcrun altool -t ios --validate-app --verbose -u "${USER}" -p @env:PASSWORD -f ${ipafile}) - step(xcrun altool -t ios --upload-app -u "${USER}" -p @env:PASSWORD -f ${ipafile}) - endif() -endif() diff -Nru ausweisapp2-2.3.1/cmake/cmd.cmake ausweisapp2-2.4.0/cmake/cmd.cmake --- ausweisapp2-2.3.1/cmake/cmd.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/cmd.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION 3.25) ########################################### #### Usage: cmake -DCMD= -P cmake/cmd.cmake @@ -16,42 +16,13 @@ endif() endfunction() +function(CHECK_WIX_LOG EXPECTED_LOGGING) + list(LENGTH EXPECTED_LOGGING EXPECTED_COUNT) -function(HASH) - if(NOT FILES) - message(FATAL_ERROR "You need to specify 'FILES'") - endif() - - if(NOT ALGORITHM) - set(ALGORITHM SHA256) - endif() - string(TOLOWER "${ALGORITHM}" HASHFILE_ENDING) - - file(GLOB GLOBBED_FILES RELATIVE "${CMAKE_CURRENT_BINARY_DIR}" "${FILES}") - - foreach(f ${GLOBBED_FILES}) - file(${ALGORITHM} ${f} fHash) - set(OUTPUT "${fHash} ${f}") - message(STDOUT ${OUTPUT}) - if(CREATE_FILE) - file(WRITE ${f}.${HASHFILE_ENDING} "${OUTPUT}\n") - endif() - endforeach() -endfunction() - -function(CHECK_WIX_WARNING) - list(APPEND EXPECTED_WARNINGS "CNDL1077.*WixShellExecTarget.*INSTALL_ROOT") - list(APPEND EXPECTED_WARNINGS "CNDL1077.*WixShellExecTarget.*ProductName") - list(APPEND EXPECTED_WARNINGS "LGHT1076.*ICE03.*CustomInstallDirDlg.SystemSettingsCheckBox") - list(APPEND EXPECTED_WARNINGS "LGHT1076.*ICE30.*AusweisApp2.*ProxyService") - list(APPEND EXPECTED_WARNINGS "LGHT1076.*ICE30.*AusweisApp2.*ProxyService") - list(APPEND EXPECTED_WARNINGS "LGHT1076.*ICE61.*product.*version") - list(LENGTH EXPECTED_WARNINGS EXPECTED_COUNT) - - file(STRINGS "${FILE}" WIX_WARNINGS REGEX "warning") + file(STRINGS "${FILE}" WIX_WARNINGS REGEX "warning|error") foreach(m ${WIX_WARNINGS}) unset(KNOWN_WARNING) - foreach(e ${EXPECTED_WARNINGS}) + foreach(e ${EXPECTED_LOGGING}) string(REGEX MATCH "${e}" KNOWN_WARNING "${m}") if(KNOWN_WARNING) MATH(EXPR WARNING_COUNT "${WARNING_COUNT}+1") @@ -80,74 +51,26 @@ endif() endfunction() +function(CHECK_WIX_MSI) + list(APPEND EXPECTED_LOGGING "warning WIX1077.*WixShellExecTarget.*INSTALL_ROOT") + list(APPEND EXPECTED_LOGGING "warning WIX1077.*WixShellExecTarget.*ProductName") -function(DEPLOY_NEXUS) - find_program(MVN_BIN mvn) - if(NOT MVN_BIN) - message(FATAL_ERROR "Cannot find mvn") - endif() - - set(SETTINGS_XML " - - - nexus - \${env.NEXUS_USERNAME} - \${env.NEXUS_PSW} - - - central - \${env.CENTRAL_USERNAME} - \${env.CENTRAL_PSW} - - - ") - file(WRITE settings.xml "${SETTINGS_XML}") - - function(get_file _suffix _out_var) - file(GLOB file RELATIVE ${CMAKE_BINARY_DIR} ${_suffix}) - - list(LENGTH file list_length) - if(list_length GREATER 1) - message(FATAL_ERROR "Found more than one entry: ${file}") - elseif(asc_length EQUAL 0) - message(FATAL_ERROR "File ${file} not found. Maybe signature is missing?") - endif() - - set(${_out_var} ${file} PARENT_SCOPE) - endfunction() - - get_file("*.aar" FILE_AAR) - get_file("*.pom" FILE_POM) - get_file("*-sources.jar" FILE_JAR) - - file(STRINGS "${FILE_POM}" is_snapshot REGEX ".+-SNAPSHOT") - if(is_snapshot) - set(NEXUS_URL https://repo.govkg.de/repository/ausweisapp-snapshots) - else() - set(NEXUS_URL https://repo.govkg.de/repository/ausweisapp-releases) - endif() - - set(MVN_CMD ${MVN_BIN} deploy:3.1.3:deploy-file -Dfile=${FILE_AAR} -DpomFile=${FILE_POM} -Dsources=${FILE_JAR} --settings settings.xml) - EXECUTE(${MVN_CMD} -DrepositoryId=nexus -Durl=${NEXUS_URL}) - - if(PUBLISH AND NOT is_snapshot) - set(CENTRAL_PARAMS -DrepositoryId=central -Durl=https://s01.oss.sonatype.org/service/local/staging/deploy/maven2) - EXECUTE(${MVN_CMD} ${CENTRAL_PARAMS}) - - get_file("*.aar.asc" FILE_AAR_ASC) - get_file("*.pom.asc" FILE_POM_ASC) - get_file("*-sources.jar.asc" FILE_SOURCES_ASC) - - function(mvn_upload _file _packaging _classifier) - EXECUTE(${MVN_BIN} deploy:3.1.3:deploy-file -Dfile=${_file} -Dpackaging=${_packaging} -Dclassifier=${_classifier} -DpomFile=${FILE_POM} ${CENTRAL_PARAMS} --settings settings.xml) - endfunction() + CHECK_WIX_LOG("${EXPECTED_LOGGING}") +endfunction() - mvn_upload("${FILE_AAR_ASC}" "aar.asc" "") - mvn_upload("${FILE_POM_ASC}" "pom.asc" "") - mvn_upload("${FILE_SOURCES_ASC}" "jar.asc" "sources") - endif() +function(CHECK_WIX_VALIDATION) + list(APPEND EXPECTED_LOGGING "warning WIX1076.*ICE03.*String overflow.*CustomInstallDirDlg\\.SystemSettingsCheckBox") + list(APPEND EXPECTED_LOGGING "warning WIX1076.*ICE30.*AusweisApp2.*ProxyService.*mutually exclusive") + list(APPEND EXPECTED_LOGGING "warning WIX1076.*ICE30.*AusweisApp2.*ProxyService.*mutually exclusive") + list(APPEND EXPECTED_LOGGING "error WIX0204.*ICE38.*DesktopShortcut.*user profile.*KeyPath.*HKCU") + list(APPEND EXPECTED_LOGGING "error WIX0204.*ICE38.*StartmenuShortcut.*user profile.*KeyPath.*HKCU") + list(APPEND EXPECTED_LOGGING "error WIX0204.*ICE43.*DesktopShortcut.*non-advertised shortcuts.*KeyPath.*HKCU") + list(APPEND EXPECTED_LOGGING "error WIX0204.*ICE43.*StartmenuShortcut.*non-advertised shortcuts.*KeyPath.*HKCU") + list(APPEND EXPECTED_LOGGING "error WIX0204.*ICE57.*'DesktopShortcut' has both.*data with a per-machine KeyPath") + list(APPEND EXPECTED_LOGGING "error WIX0204.*ICE57.*'StartmenuShortcut' has both.*data with a per-machine KeyPath") + list(APPEND EXPECTED_LOGGING "warning WIX1076.*ICE61.*This product should remove only older versions of itself.") - file(REMOVE settings.xml) + CHECK_WIX_LOG("${EXPECTED_LOGGING}") endfunction() function(CHECK_FAILURE_CODES) @@ -304,6 +227,119 @@ endif() endfunction() +function(CHECK_QMLENUMS) + file(GLOB_RECURSE QML "${CMAKE_CURRENT_BINARY_DIR}/*.qml") + + foreach(file ${QML}) + file(STRINGS "${file}" filecontent) + unset(ENUM_NAME) + + foreach(line ${filecontent}) + if(ENUM_NAME) + if(line MATCHES "}") + unset(ENUM_NAME) + elseif(line MATCHES "([A-Za-z0-9_]+)") + list(APPEND ENUMS "${ENUM_NAME}.${CMAKE_MATCH_1}") + endif() + elseif(NOT ENUM_NAME AND line MATCHES "enum ([A-Za-z]+) {") + cmake_path(GET file STEM stem) + set(ENUM_NAME "${stem}.${CMAKE_MATCH_1}") + endif() + endforeach() + endforeach() + + unset(bad_enum_regex) + foreach(enum IN LISTS ENUMS) + cmake_path(GET enum STEM enum_component) + cmake_path(GET enum EXTENSION LAST_ONLY enum_value) + list(APPEND bad_enum_regex ${enum_component}${enum_value}) + endforeach() + list(JOIN bad_enum_regex "|" bad_enum_regex) + + foreach(file ${QML}) + file(STRINGS "${file}" filecontent REGEX "(${bad_enum_regex})") + if(filecontent) + message(STATUS "File contains wrong values: ${file}") + set(failed TRUE) + + foreach(line ${filecontent}) + foreach(enum IN LISTS ENUMS) + cmake_path(GET enum STEM enum_component) + cmake_path(GET enum EXTENSION LAST_ONLY enum_value) + if(line MATCHES "${enum_component}${enum_value}") + message(" Value: ${enum_component}${enum_value}\n Enum: ${enum}\n Line: ${line}") + endif() + endforeach() + endforeach() + endif() + endforeach() + + if(failed) + message(FATAL_ERROR "Enum in QML is not scoped") + endif() +endfunction() + +function(GENERATE_APPCAST) + set(APPCAST_URL https://updates.autentapp.de) + set(APPCAST_ITEM " + { + \"date\": \"APPCAST_DATE\", + \"platform\": \"APPCAST_PLATFORM\", + \"minimum_platform\": \"APPCAST_MINIMUM_PLATFORM\", + \"version\": \"APPCAST_VERSION\", + \"url\": \"APPCAST_URL\", + \"size\": APPCAST_SIZE, + \"checksum\": \"APPCAST_CHECKSUM\", + \"notes\": \"APPCAST_NOTES\" + }") + set(APPCAST "{ + \"items\": + [APPCAST_ITEMS + ] +}") + + macro(ADD_APPCAST_FILE _files _system _min) + string(TIMESTAMP APPCAST_DATE "%Y-%m-%dT%H:%M:%S") + + foreach(filePath ${_files}) + file(SIZE ${filePath} fileSize) + get_filename_component(file ${filePath} NAME) + + if(NOT DEFINED fileSize) + message(FATAL_ERROR "Cannot get file size of: ${file}") + endif() + + message(STATUS "Processing: ${file}") + + string(REPLACE "AusweisApp-" "" APPCAST_FILE_VERSION ${file}) + string(REPLACE ".msi" "" APPCAST_FILE_VERSION ${APPCAST_FILE_VERSION}) + + set(item ${APPCAST_ITEM}) + string(REPLACE "APPCAST_DATE" "${APPCAST_DATE}" item ${item}) + string(REPLACE "APPCAST_PLATFORM" ${_system} item ${item}) + string(REPLACE "APPCAST_MINIMUM_PLATFORM" ${_min} item ${item}) + string(REPLACE "APPCAST_VERSION" "${APPCAST_FILE_VERSION}" item ${item}) + string(REPLACE "APPCAST_URL" "${APPCAST_URL}/${file}" item ${item}) + string(REPLACE "APPCAST_SIZE" "${fileSize}" item ${item}) + string(REPLACE "APPCAST_CHECKSUM" "${APPCAST_URL}/${file}.sha256" item ${item}) + string(REPLACE "APPCAST_NOTES" "${APPCAST_URL}/ReleaseNotes.html" item ${item}) + + set(APPCAST_ITEMS "${APPCAST_ITEMS}${item},") + endforeach() + endmacro() + + file(GLOB MSI_FILES ${CMAKE_BINARY_DIR}/*.msi) + if(MSI_FILES) + ADD_APPCAST_FILE("${MSI_FILES}" "win" "10") + endif() + + if(APPCAST_ITEMS) + string(REGEX REPLACE ",$" "" APPCAST_ITEMS "${APPCAST_ITEMS}") + string(REPLACE "APPCAST_ITEMS" "${APPCAST_ITEMS}" APPCAST "${APPCAST}") + file(CONFIGURE OUTPUT "${CMAKE_BINARY_DIR}/AppcastInfo.json" CONTENT "${APPCAST}" @ONLY NEWLINE_STYLE UNIX) + endif() +endfunction() + if(NOT CMD) message(FATAL_ERROR "You need to specify 'CMD'") diff -Nru ausweisapp2-2.3.1/cmake/iOS.bundles.cmake.in ausweisapp2-2.4.0/cmake/iOS.bundles.cmake.in --- ausweisapp2-2.3.1/cmake/iOS.bundles.cmake.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/iOS.bundles.cmake.in 2025-10-30 10:10:48.000000000 +0000 @@ -1,9 +1,14 @@ +set(TARGET_FILE @CMAKE_BINARY_DIR@/@CPACK_PACKAGE_FILE_NAME@) +if(SUFFIX) + set(TARGET_FILE ${TARGET_FILE}.${SUFFIX}) +endif() + function(SELF_GENERATED) function(FIND_BUNDLE _name _out_bundle _out_parent_dir) set(BUNDLE_DIRS "${CONFIG}-iphoneos;${CONFIG}-iphonesimulator;${CONFIG};UninstalledProducts;UninstalledProducts/iphoneos;UninstalledProducts/iphonesimulator") foreach(dir ${BUNDLE_DIRS}) - set(tmpDir @PROJECT_BINARY_DIR@/src/${dir}) + set(tmpDir @CMAKE_BINARY_DIR@/src/${dir}) set(tmpBundleDir ${tmpDir}/${_name}) if(EXISTS "${tmpBundleDir}") set(${_out_bundle} "${tmpBundleDir}" PARENT_SCOPE) @@ -28,8 +33,8 @@ endif() if(INTEGRATED_SDK) - configure_file("@PROJECT_SOURCE_DIR@/resources/packaging/ios/module.modulemap" "${BundleDir}/Modules/module.modulemap" COPYONLY) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar cf "@PROJECT_BINARY_DIR@/@CPACK_PACKAGE_FILE_NAME@.framework.zip" --format=zip ${Bundle} WORKING_DIRECTORY ${ParentDir}) + configure_file("@CMAKE_SOURCE_DIR@/resources/packaging/ios/module.modulemap" "${BundleDir}/Modules/module.modulemap" COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar cf "${TARGET_FILE}.framework.zip" --format=zip ${Bundle} WORKING_DIRECTORY ${ParentDir}) else() file(GLOB_RECURSE FILES RELATIVE "${CMAKE_BINARY_DIR}" *.dylib) foreach(f ${FILES}) @@ -45,35 +50,31 @@ endif() execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${BundleDir} Payload/${Bundle}) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar cf "@PROJECT_BINARY_DIR@/@CPACK_PACKAGE_FILE_NAME@.ipa" --format=zip Payload) - if(CMAKE_VERSION VERSION_LESS "3.17") - execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory Payload) - else() - execute_process(COMMAND ${CMAKE_COMMAND} -E rm -r Payload) - endif() + execute_process(COMMAND ${CMAKE_COMMAND} -E tar cf "${TARGET_FILE}.ipa" --format=zip Payload) + execute_process(COMMAND ${CMAKE_COMMAND} -E rm -r Payload) endif() FIND_BUNDLE(${Bundle}.dSYM dSYM ParentDir) if(dSYM) message(STATUS "Use dSYM: ${dSYM}") - execute_process(COMMAND ${CMAKE_COMMAND} -E tar cf "@PROJECT_BINARY_DIR@/@CPACK_PACKAGE_FILE_NAME@.dSYM.zip" --format=zip ${Bundle}.dSYM WORKING_DIRECTORY ${ParentDir}) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar cf "${TARGET_FILE}.dSYM.zip" --format=zip ${Bundle}.dSYM WORKING_DIRECTORY ${ParentDir}) endif() if(ParentDir) file(GLOB SymbolMap "${ParentDir}/*.bcsymbolmap") if(SymbolMap) message(STATUS "Use bcsymbolmap: ${SymbolMap}") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${SymbolMap} @PROJECT_BINARY_DIR@/@CPACK_PACKAGE_FILE_NAME@.bcsymbolmap) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${SymbolMap} ${TARGET_FILE}.bcsymbolmap) endif() endif() endfunction() function(XCODE_GENERATED) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar cf "@PROJECT_BINARY_DIR@/@CPACK_PACKAGE_FILE_NAME@.xcarchive.zip" --format=zip @PROJECT_NAME@.xcarchive) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar cf "${TARGET_FILE}.xcarchive.zip" --format=zip @PROJECT_NAME@.xcarchive) if(INTEGRATED_SDK) message(FATAL_ERROR "Not supported") else() - execute_process(COMMAND ${CMAKE_COMMAND} -E rename @PROJECT_NAME@.ipa @CPACK_PACKAGE_FILE_NAME@.ipa) + execute_process(COMMAND ${CMAKE_COMMAND} -E rename @PROJECT_NAME@.ipa ${TARGET_FILE}.ipa) endif() endfunction() diff -Nru ausweisapp2-2.3.1/cmake/macOS.pkg.cmake ausweisapp2-2.4.0/cmake/macOS.pkg.cmake --- ausweisapp2-2.3.1/cmake/macOS.pkg.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/macOS.pkg.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13.0) +cmake_minimum_required(VERSION 3.25) find_program(XCRUN xcrun) if(NOT XCRUN) @@ -6,6 +6,6 @@ endif() set(APP_PATH ${CPACK_TEMPORARY_DIRECTORY}/${CPACK_PACKAGE_NAME}.app) -set(PKG_PATH ${CPACK_PACKAGE_DIRECTORY}/${CPACK_SOURCE_PACKAGE_FILE_NAME}.pkg) +set(PKG_PATH ${CPACK_PACKAGE_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}.pkg) execute_process(COMMAND ${XCRUN} productbuild --sign "3rd Party Mac Developer Installer: Governikus GmbH & Co. KG (G7EQCJU4BR)" --component ${APP_PATH} /Applications ${PKG_PATH}) diff -Nru ausweisapp2-2.3.1/cmake/pandoc/convert_tables.lua ausweisapp2-2.4.0/cmake/pandoc/convert_tables.lua --- ausweisapp2-2.3.1/cmake/pandoc/convert_tables.lua 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/pandoc/convert_tables.lua 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,155 @@ +local footnote_map = {} +local footnote_counter = 0 +local footnotes_collected = {} + +function printTable(tbl, indent) + indent = indent or "" + for k, v in pairs(tbl) do + if type(v) == "table" then + print(indent .. tostring(k) .. ": {") + printTable(v, indent .. " ") -- Recurse for nested tables + print(indent .. "}") + else + print(indent .. tostring(k) .. ": " .. tostring(v)) + end + end +end + + +local function inlines_to_latex(inlines) + local latex_string = {} + for _, inline in ipairs(inlines) do + table.insert(latex_string, pandoc.write(pandoc.Pandoc(inline), 'latex')) + end + return table.concat(latex_string, "") +end + + +local function collect_footnotes_and_replace(elem) + if elem.t == "Note" then + local content_as_json = pandoc.json.encode(elem.content) -- Use JSON for consistent hashing + if not footnote_map[content_as_json] then + footnote_counter = footnote_counter + 1 + footnote_map[content_as_json] = footnote_counter + table.insert(footnotes_collected, {number = footnote_counter, content = elem.content}) + end + return pandoc.RawInline('tex', '\\footnotemark[\\numexpr\\value{originalfootnotevalue}+' .. footnote_map[content_as_json] .. ']') + else + return elem + end +end + +function convert_Table(elem) + local latex_output = {} + + local caption = inlines_to_latex(elem.caption) + -- Determine column alignments and widths + local col_format_string = "" + for _, spec in ipairs(elem.colspecs) do + local align = spec[1].t + local width = spec[2] or nil -- ColWidth is optional + + local latex_align + if align == "AlignLeft" then + latex_align = "l" + elseif align == "AlignRight" then + latex_align = "r" + elseif align == "AlignCenter" then + latex_align = "c" + else -- AlignDefault + latex_align = "l" -- Default to left for consistency with Pandoc's default + end + + if width then + -- Convert Pandoc's relative width to LaTeX's absolute width (e.g., \linewidth) + -- This assumes the table will fill the text width, adjust as needed + local actual_width = string.format("%.3f\\linewidth", width) + col_format_string = col_format_string .. "p{" .. actual_width .. "}" + else + col_format_string = col_format_string .. latex_align + end + end + + table.insert(latex_output, "\\setcounter{originalfootnotevalue}{\\value{footnote}}\n") + table.insert(latex_output, "\\begin{table}[h!]\n") + table.insert(latex_output, "\\centering\n") + table.insert(latex_output, "\\rowcolors{1}{table_even_row_color}{table_odd_row_color}\n") + table.insert(latex_output, "\\caption{" .. caption .. "}\n") + table.insert(latex_output, "\\begin{tabular}{" .. col_format_string .. "}\n") + table.insert(latex_output, "\\toprule\n") + table.insert(latex_output, "\\rowcolor{table_header_color}") + + local head = elem.head + -- Table Head + if #head.rows > 0 then + for _, head_row in ipairs(head.rows) do + local cells = head_row.cells + local row_content = {} + for _, cell_data in ipairs(cells) do + local cell_contents = cell_data.contents + table.insert(row_content, inlines_to_latex(cell_contents)) + end + table.insert(latex_output, table.concat(row_content, " & ") .. " \\\\\n") + end + table.insert(latex_output, "\\midrule\n") + end + + local bodies = elem.bodies + -- Table Bodies + for _, body_data in ipairs(bodies) do + local body_rows = body_data.body + for _, row_data in ipairs(body_rows) do + local cells = row_data.cells + local row_content = {} + for _, cell_data in ipairs(cells) do + local cell_blocks = cell_data.content + local cell_content = {} + for _, block in ipairs(cell_blocks) do + local processed_contents = pandoc.walk_block(block, {Note = collect_footnotes_and_replace}) + table.insert(cell_content, processed_contents) + end + local cells_in_latex = inlines_to_latex(cell_content) + table.insert(row_content, cells_in_latex) + end + table.insert(latex_output, table.concat(row_content, " & ") .. " \\\\\n") + end + end + + table.insert(latex_output, "\\bottomrule\n") + table.insert(latex_output, "\\end{tabular}\n") + + table.insert(latex_output, "\\end{table}\n") + if #footnotes_collected > 0 then + for _, footnote in ipairs(footnotes_collected) do + table.insert(latex_output, "\\footnotetext[\\numexpr\\value{originalfootnotevalue}+" .. footnote.number .. "]{" .. inlines_to_latex(footnote.content) .. "}\n") + end + end + table.insert(latex_output, "\\setcounter{footnote}{\\numexpr\\value{originalfootnotevalue} + " .. footnote_counter .. "}\n") + footnote_map = {} + footnote_counter = 0 + footnotes_collected = {} + + return pandoc.RawBlock("latex", table.concat(latex_output)) +end + + +function convert_BlockQuote(elem) + if elem.content and elem.content[1].t == "Table" then + return convert_Table(elem.content[1]) + end + return elem +end + + +function Pandoc(doc) + local meta = doc.meta + + local appName = meta['app_name'] + + local handlers = { + BlockQuote = convert_BlockQuote, + Table = convert_Table + } + + return doc:walk(handlers) +end \ No newline at end of file diff -Nru ausweisapp2-2.3.1/cmake/pandoc/custom_filter.lua ausweisapp2-2.4.0/cmake/pandoc/custom_filter.lua --- ausweisapp2-2.3.1/cmake/pandoc/custom_filter.lua 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/pandoc/custom_filter.lua 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,131 @@ +-- globals +local toctree_seen = false + +function transform_colored_box(elem, tex_environment, skip_first_elem) + -- table.unpack with an index eats the contents of the block after the first paragraph, create list without title manually + local new_content = {} + for i, e in ipairs(elem.content) do + if (not (i == 1 and skip_first_elem)) then + table.insert(new_content, e) + end + end + + table.insert(new_content, 1, pandoc.RawBlock("tex", '\\begin{' .. tex_environment .. '}')) + table.insert(new_content, pandoc.RawBlock("tex", '\\end{' .. tex_environment .. '}')) + return new_content +end + +function string_concat(current_string, new_string) + if current_string == '' then + return new_string + else + return current_string .. '\n' .. new_string + end +end + +function Pandoc(doc) + local handlers = { + Div = function (elem) + if elem.attr and elem.attr.classes then + if elem.attr.classes[1] == 'only' then + if (elem.content[1].content.text == 'latex') then + return elem + else + return pandoc.RawBlock('tex', '') + end + elseif elem.attr.classes[1] == 'important' then + return transform_colored_box(elem, 'ImportantBox', true) + elseif elem.attr.classes[1] == 'seealso' then + return transform_colored_box(elem, 'SeeAlsoBox', false) + elseif elem.attr.classes[1] == 'note' then + return transform_colored_box(elem, 'NoteBox', true) + end + else + return elem + end + end, + Code = function (elem) + if elem.attr.classes[1] == 'interpreted-text' then + if elem.attr.attributes['role'] == 'doc' or elem.attr.attributes['role'] == 'ref' or elem.attr.attributes['role'] == 'numref' then + local label = elem.text + local content = elem.text + local start_index, end_index, before_brackets, inside_brackets = string.find(elem.text, "(.*) <([^<>]+)>") + local latex_output = '' + if (start_index) then + label = inside_brackets + content = before_brackets + latex_output = '\\hyperref[' .. label .. ']{' .. content .. '} (\\pagename{} \\pageref*{' .. label .. '})' + else + latex_output = '\\hyperref[' .. label .. ']{\\nameref{' .. content .. '}} (\\pagename{} \\pageref*{' .. label .. '})' + end + return pandoc.RawInline('tex', latex_output) + else + return elem + end + else + return elem + end + end, + CodeBlock = function (elem) + -- These changes to CodeBlock are not ideal as it breaks pandocs automatic syntax highlighting. + -- In most cases it works and a11y can be achieved, we therefore keep it this way until pdftag supports vfextra linebreaks or the listings package. + + -- Don't color json CodeBlocks as they are often broken up over multiple pages, leading to broken syntax highlighting. + if elem.attr.classes[1] == 'json' then + elem.attr.classes[1] = '' + end + + -- Split long lines in CodeBlocks, Split CodeBlocks + local output = '' + for token in string.gmatch(elem.text, "[^\n]+") do + local chunk_size = 80 + local str_len = string.len(token) + local chunks = {} + local separator = "â£\n→" + + if str_len <= chunk_size then + output = string_concat(output, token) + else + for i = 1, str_len, chunk_size do + local chunk = string.sub(token, i, math.min(i + chunk_size - 1, str_len)) + table.insert(chunks, chunk) + end + + output = string_concat(output, table.concat(chunks, separator)) + end + end + + -- Split too many lines into multiple code blocks. + local blocks = {} + local current_output = '' + local count = 0 + local count_limit = 46 + for token in string.gmatch(output, "[^\n]+") do + current_output = string_concat(current_output, token) + count = count + 1 + if count >= count_limit then + table.insert(blocks, pandoc.CodeBlock(current_output, elem.attr)) + table.insert(blocks, pandoc.RawBlock('tex', '\\continuesname\n')) + table.insert(blocks, pandoc.RawBlock('tex', '\\clearpage\n')) + table.insert(blocks, pandoc.RawBlock('tex', '\\continuedname\n')) + count = 0 + current_output = '' + end + end + + if current_output ~= '' then + table.insert(blocks, pandoc.CodeBlock(current_output, elem.attr)) + current_output = '' + end + + -- End paragraph before code block. Fixes problems with `\paragraph` preceding CodeBlocks. + table.insert(blocks, 1, pandoc.RawBlock('tex', '\n\n\\noindent\n')) + -- End paragraph after code block. Fixes problems with sections following CodeBlocks. + table.insert(blocks, pandoc.RawBlock('tex', '\n\n\\noindent\n')) + + return blocks + end, + } + + return doc:walk(handlers) +end diff -Nru ausweisapp2-2.3.1/cmake/pandoc/preprocess_rst.py ausweisapp2-2.4.0/cmake/pandoc/preprocess_rst.py --- ausweisapp2-2.3.1/cmake/pandoc/preprocess_rst.py 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/pandoc/preprocess_rst.py 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,866 @@ +#!/usr/bin/env python +import sys +import re +import os +import argparse +from abc import ABC, abstractmethod + +first_toctree_found = False +main_hierarchy = ['#', '*', '=', '-', '"', '^', '~'] + + +def wrap_latex(latex_strings: list[str]) -> list[str]: + """ + Wrap latex strings in a rst raw latex block and indent the code. + """ + output = [] + output.append('') + output.append('.. raw:: latex') + output.append('') + for latex_string in latex_strings: + output.append(f' {latex_string}') + output.append('') + return output + + +def is_header(line): + """ + Check if the line is a heading by looking for 3 or more repeating + characters from the main list and checking if it is the only character + (to not confuse them with tables). + """ + return ( + any(line.startswith(c * 3) for c in main_hierarchy) + and len(set(line.rstrip())) == 1 + ) + + +def get_file_heading_hierarchy(input_lines): + """ + Identifies the heading characters used in a file and returns them in the + order of their appearance. + """ + hierarchy = [] + for line in input_lines: + if is_header(line): + if line[0] not in hierarchy: + hierarchy.append(line[0]) + return hierarchy + + +def convert_file_heading_hierarchy_to_main( + input_lines, local_hierarchy, starting_level +): + """ + Rewrites the headings in a file to conform to a main heading hierarchy. + """ + output_lines = [] + for line in input_lines: + if is_header(line): + character = line.strip()[0] + level = local_hierarchy.index(character) + new_character = main_hierarchy[starting_level + level] + output_lines.append( + line.rstrip().replace(character, new_character) + ) + else: + output_lines.append(line.rstrip()) + return output_lines + + +def parse_po_file(translation_data): + """ + Parses translation data from a .po file + """ + translations = {} + # Split the file content into entry blocks based on one or more empty + # lines. Skip the first block, which is the file header. + blocks = re.split(r'\n\s*\n', translation_data.strip())[1:] + + for block in blocks: + # Skip any empty blocks that might result from splitting + if not block.strip(): + continue + + lines = block.splitlines() + msgid_lines = [] + msgstr_lines = [] + current_key = None + + for line in lines: + line = line.strip() + # Skip comments and empty lines + if line.startswith('#') or not line: + continue + + if line.startswith('msgid '): + current_key = 'msgid' + match = re.search(r'"(.*)"', line) + if match: + msgid_lines.append(match.group(1)) + elif line.startswith('msgstr '): + current_key = 'msgstr' + match = re.search(r'"(.*)"', line) + if match: + msgstr_lines.append(match.group(1)) + elif line.startswith('"') and current_key: + match = re.search(r'"(.*)"', line) + if match: + content = match.group(1) + if current_key == 'msgid': + msgid_lines.append(content) + elif current_key == 'msgstr': + msgstr_lines.append(content) + if msgid_lines: + final_msgid = ( + ''.join(msgid_lines).replace('\\"', '"').replace('\\\\', '\\') + ) + final_msgstr = ( + ''.join(msgstr_lines).replace('\\"', '"').replace('\\\\', '\\') + ) + + if final_msgid and final_msgstr: + translations[final_msgid] = final_msgstr + + return translations + + +def translate_lines(input_lines, translation_file_path): + """ + Translate the input_lines using the .po file at translation_file_path. + """ + try: + with open(translation_file_path, 'r', encoding='utf-8') as infile: + translation_data = infile.read() + except FileNotFoundError: + print(f'Error: Input file "{translation_file_path}" not found.') + sys.exit(1) + except Exception as e: + print( + f'Error: Input file "{translation_file_path}" could not accessed: ' + + f'{e}' + ) + sys.exit(1) + + translation_map = parse_po_file(translation_data) + + merged_lines = [] + line_idx = 0 + previous_line = '' + while line_idx < len(input_lines): + current_line = input_lines[line_idx] + # Merge the current line with the previous line if + # a) The previous line ended on an alphanumerical character or + # punctuation + # b) The current line starts on an alphanumerical character or + # punctuation, disregarding spaces (except when we are in blocks, + # like toctree) + # c) The current line is not a header underline + # d) The current line does not start with '| ' (distinguish between + # item list and replace-marker) + marker_for_merge = '[a-zA-Z0-9\\.,\\"-\\(\\)\\|:\\\\]' + if ( + re.search(f'{marker_for_merge}$', previous_line.rstrip('\n')) + and ( + re.match(f'^{marker_for_merge}', current_line) + or re.match(f'^ \\s+{marker_for_merge}', current_line) + ) + and not is_header(current_line) + and not re.match(r'^\s*\| ', current_line) + ): + new_line = current_line.rstrip('\n').lstrip(' ') + merged_lines[-1] = f'{previous_line} {new_line}' + else: + merged_lines.append(current_line.rstrip('\n')) + previous_line = merged_lines[-1] + line_idx += 1 + + output_lines = [] + for original_line in merged_lines: + translated_lines = [] + for key in translation_map.keys(): + if key in original_line: + translated_lines.append( + ( + original_line.rstrip().replace( + key, translation_map[key] + ), + len(key), + ) + ) + + if translated_lines: + # Use the longest translated line to not use partial translations. + # Multiple translations per line are currently not supported. + translated_lines.sort(key=lambda x: x[1], reverse=True) + output_lines.append(translated_lines[0][0]) + else: + output_lines.append(original_line) + + line_idx = 0 + while line_idx + 1 < len(output_lines): + title = output_lines[line_idx] + underline = output_lines[line_idx + 1] + if is_header(underline): + character = underline[0] + output_lines[line_idx + 1] = character * len(title) + line_idx += 1 + + return output_lines + + +def include_file(filepath, parent_filepath, current_level, translate): + """ + Resolves the path for an included file and processes it for flattening. + """ + dir_path = os.path.dirname(parent_filepath) + file_path = os.path.join(dir_path, filepath) + if not os.path.exists(file_path): + print(f'Error: Referenced file {file_path} does not exit.') + sys.exit(1) + + return flatten_files(file_path, current_level, translate) + + +def flatten_include( + input_lines, start_idx, current_level, parent_filepath, translate +): + """ + Helper function parsing the include directive. Includes recursively the + referenced files. + """ + line_idx = start_idx + output_lines = [] + + match = re.match(r'.. include:: (.*)', input_lines[line_idx].strip()) + if not match: + print( + 'Error: could not parse include directive,' + + f'{parent_filepath} line {line_idx}' + ) + sys.exit(1) + output_lines.extend( + include_file(match.group(1), parent_filepath, current_level, translate) + ) + output_lines.append('') + line_idx += 1 + return output_lines, line_idx + + +def flatten_toctree( + input_lines, start_idx, current_level, parent_filepath, translate +): + """ + Helper function parsing the toctree directive. Adds the table of contents + at the position of the first toctree, parses the options and includes + recursively the referenced files. + """ + global first_toctree_found + line_idx = start_idx + 1 + output_lines = [] + + # For the first toctree found, insert a LaTeX table of contents. + if not first_toctree_found: + output_lines.extend(wrap_latex(['\\tableofcontents', '\\newpage'])) + first_toctree_found = True + + options = {} + # Parse toctree options. + while line_idx < len(input_lines) and input_lines[line_idx].strip() != '': + option_match = re.match( + ':([a-zA-Z0-9]+): (.*)', + input_lines[line_idx].strip(), + ) + if option_match: + option_key = option_match.group(1) + option_value = option_match.group(2) + options[option_key] = option_value + line_idx += 1 + line_idx += 1 + + if 'caption' in options.keys(): + caption = options['caption'] + output_lines.append('') + output_lines.append(caption) + output_lines.append(main_hierarchy[current_level] * len(caption)) + current_level += 1 + output_lines.append('') + + # Recursively include and process files listed in the toctree. + while line_idx < len(input_lines) and input_lines[line_idx].strip() != '': + file_name = input_lines[line_idx].strip() + output_lines.extend( + include_file( + f'{file_name}.rst', parent_filepath, current_level, translate + ) + ) + output_lines.append('') + line_idx += 1 + + return output_lines, line_idx + + +def flatten_files(input_filepath, starting_level, translate): + """ + Recursively flattens a ReStructuredText file by replacing `.. toctree::` + and `.. include::` directives with their content, adjusting heading levels + accordingly. + """ + + # read the input file + try: + with open(input_filepath, 'r', encoding='utf-8') as infile: + input_lines = infile.readlines() + except FileNotFoundError: + print(f'Error: Input file "{input_filepath}" not found.') + sys.exit(1) + except Exception as e: + print( + f'Error: Input file "{input_filepath}" could not be accessed: {e}' + ) + sys.exit(1) + + if translate: + dir_path = os.path.dirname(input_filepath) + filename = os.path.basename(input_filepath) + filename_without_ext = os.path.splitext(filename)[0] + translation_file_path = os.path.join( + dir_path, + f'locales/{translate}/LC_MESSAGES/{filename_without_ext}.po', + ) + if not os.path.exists(translation_file_path): + print( + f'Error: Translation {translate} for {input_filepath} does not' + + f'exit at {translation_file_path}.' + ) + sys.exit(1) + input_lines = translate_lines(input_lines, translation_file_path) + + # Determine the existing heading structure and adjust it to fit the main + # document's hierarchy. + heading_hierarchy = get_file_heading_hierarchy(input_lines) + input_lines = convert_file_heading_hierarchy_to_main( + input_lines, heading_hierarchy, starting_level + ) + + output_lines = [] + line_idx = 0 + current_level = starting_level + + # Process each line of the input file. + while line_idx < len(input_lines): + # If the line is a heading, update the current heading level. + if is_header(input_lines[line_idx]): + current_level = main_hierarchy.index( + input_lines[line_idx].strip()[0] + ) + output_lines.append(input_lines[line_idx].rstrip()) + line_idx += 1 + # If the line is a toctree directive, process the included files. + elif input_lines[line_idx].strip().startswith('.. toctree::'): + toctree_lines, line_idx = flatten_toctree( + input_lines, line_idx, current_level, input_filepath, translate + ) + output_lines.extend(toctree_lines) + # If the line is an include directive, recursively include and process + # the specified file. + elif input_lines[line_idx].strip().startswith('.. include::'): + include_lines, line_idx = flatten_include( + input_lines, line_idx, current_level, input_filepath, translate + ) + output_lines.extend(include_lines) + # Otherwise, add the line to the output. + else: + output_lines.append(input_lines[line_idx].rstrip()) + line_idx += 1 + return output_lines + + +def replace_placeholder(input_lines, old, new): + """ + Replace every occurrence of || + """ + output_lines = [] + idx = 0 + while idx < len(input_lines): + line = input_lines[idx] + old_guarded = f'|{old}|' + new_line = line.replace(old_guarded, new) + output_lines.append(new_line) + if ( + old_guarded in line + and idx + 1 < len(input_lines) + and is_header(input_lines[idx + 1]) + ): + # Correct header underline length + header_line = input_lines[idx + 1] + header_character = header_line[0] + output_lines.append(header_character * len(new_line)) + idx += 1 + idx += 1 + + return output_lines + + +def inline_toc_header(input_lines): + """ + The rst-files contain a manual ToC header which should + 1. Not be listed in the ToC + 2. Not influence the whole section hierarchy by grouping the whole + document under it + This function replaces it with a raw LaTeX header + """ + output_lines = [] + line_idx = 0 + while line_idx < len(input_lines) - 1: + # Check if the line is a heading by looking for 3 or more repeating + # characters from the main list. + possible_header = [ + 'Table of contents', + 'Inhaltsverzeichnis', + 'Release Notes', + ] + + if any( + input_lines[line_idx].strip() == title for title in possible_header + ) and is_header(input_lines[line_idx + 1]): + output_lines.extend( + wrap_latex([f'\\section*{{{input_lines[line_idx].strip()}}}']) + ) + line_idx += 2 + else: + output_lines.append(input_lines[line_idx].rstrip()) + line_idx += 1 + + return output_lines + + +class BaseElement(ABC): + @abstractmethod + def to_rst(self) -> list[str]: + pass + + +class Label(BaseElement): + def __init__(self, input_string: str): + super().__init__() + self.label_id: str | None = self._parse_string(input_string) + + def _parse_string(self, label_line: str) -> str | None: + label_match = re.match(r'.. _(.*):', label_line) + if label_match: + return label_match.group(1) + return None + + def to_rst(self) -> list[str]: + return wrap_latex(self.to_latex()) + + def to_latex(self) -> list[str]: + return [f'\\label{{{self.label_id}}}'] + + +class Paragraph(BaseElement): + def __init__(self, strings: list[str]): + super().__init__() + self.strings = strings + + def to_rst(self): + return self.strings + + +class CSVTable(BaseElement): + def __init__(self, label: Label, table_lines: list[str]): + super().__init__() + self.label = label + self._parse_table_lines(table_lines) + + def _parse_table_lines(self, table_lines): + line_idx = 0 + self.table_title_match = re.match( + r'.. csv-table:: (.*)', table_lines[line_idx] + ) + + line_idx += 1 + self.csv_options = {} + # Parse options + while ( + line_idx < len(table_lines) and table_lines[line_idx].strip() != '' + ): + option_match = re.match( + ':([a-zA-Z0-9]+): (.*)', + table_lines[line_idx].strip(), + ) + if option_match: + option_key = option_match.group(1) + option_value = option_match.group(2) + self.csv_options[option_key] = option_value + line_idx += 1 + + line_idx += 1 + self.csv_rows = [] + # Split csv strings + while ( + line_idx < len(table_lines) + and not table_lines[line_idx].strip() == '' + ): + self.csv_rows.append( + self.split_csv_string(table_lines[line_idx].strip()) + ) + line_idx += 1 + + line_idx += 1 + self.footnotes = {} + # Parse footnote definitions + if line_idx < len(table_lines) and table_lines[ + line_idx + ].strip().startswith('.. ['): + while line_idx < len(table_lines) and table_lines[ + line_idx + ].strip().startswith('.. ['): + match = re.match( + r'.. \[(#.+?)\] (.*)', + table_lines[line_idx].strip(), + ) + if match: + footnote_label = match.group(1) + footnote_text = match.group(2) + self.footnotes[footnote_label] = footnote_text + line_idx += 1 + while table_lines[line_idx].strip() and not table_lines[ + line_idx + ].strip().startswith('.. ['): + self.footnotes[footnote_label] = ( + self.footnotes[footnote_label] + + ' ' + + table_lines[line_idx].strip() + ) + line_idx += 1 + + def generate_labeled_tex_table(self): + """ + Generate raw LaTeX table + """ + latex_table = [ + '\\setcounter{originalfootnotevalue}{\\value{footnote}}', + '\\begin{table}[h!]', + '\\centering', + '\\rowcolors{1}{table_even_row_color}{table_odd_row_color}', + ] + if self.table_title_match: + latex_table.append( + f'\\caption{{{self.table_title_match.group(1)}}}' + ) + if self.label: + latex_table.extend(self.label.to_latex()) + + num_cols = 0 + if self.csv_rows: + num_cols = max(len(row) for row in self.csv_rows) + if num_cols > 0: + # Define column widths + if 'widths' in self.csv_options: + widths = self.csv_options['widths'].split(', ') + columns = [ + f'p{{{int(width) / 120.0}\\linewidth}}' for width in widths + ] + latex_table.append( + '\\begin{tabular}{' + ' '.join(columns) + '}' + ) + else: + latex_table.append('\\begin{tabular}{' + 'l' * num_cols + '}') + latex_table.append('\\toprule') + + # Define column titles + if 'header' in self.csv_options: + latex_table.append('\\rowcolor{table_header_color}') + header_list = self.csv_options['header'].split(', ') + latex_table.append( + ' & '.join([header.strip('"') for header in header_list]) + + ' \\\\' + ) + latex_table.append('\\midrule') + + # Add rows + for row in self.csv_rows: + latex_row = [] + for cell in row: + # Handle footnotes in cells + def replace_footnote(match): + footnote_ref = match.group(1) + if footnote_ref in self.footnotes: + key_list = list(self.footnotes.keys()) + idx = key_list.index(footnote_ref) + 1 + number = ( + '\\numexpr\\value{originalfootnotevalue}' + + f'{idx}' + ) + return f'\\footnotemark[{number}]' + return match.group(0) + + latex_cell = re.sub(r'\[(#.+?)\]_', replace_footnote, cell) + + # Remove string delimiters + latex_cell = latex_cell.strip("'") + # Escape asterisk + latex_cell = latex_cell.replace('\\*', '$*$') + latex_row.append(latex_cell) + latex_table.append(' & '.join(latex_row) + ' \\\\') + + latex_table.append('\\bottomrule') + latex_table.append('\\end{tabular}') + latex_table.append('\\end{table}') + + # Append footnotes + if self.footnotes: + number_string = '\\numexpr\\value{originalfootnotevalue}+' + footnote_counter = 1 + for _, text in self.footnotes.items(): + number = number_string + f'{footnote_counter}' + latex_table.append(f'\\footnotetext[{number}]{{{text}}}') + footnote_counter += 1 + new_counter = number_string + f'{footnote_counter}' + latex_table.append( + f'\\setcounter{{footnote}}{{{new_counter}}}' + ) + latex_table.append('') + + return latex_table + + def split_csv_string(self, s): + """ + Manually parses a single line of a CSV string, handling quoted fields. + The csv module cannot correctly handle delimiters in strings. + """ + delimiter = ',' + quote = '"' + row = [] + in_string = False + current_string = '' + for c in s: + if c == quote: + if in_string: + in_string = False + else: + in_string = True + current_string = current_string + c + elif c == delimiter and not in_string: + row.append(current_string.strip().strip(quote)) + current_string = '' + else: + current_string = current_string + c + if current_string and not in_string: + row.append(current_string.strip().strip(quote)) + return row + + def to_rst(self): + return wrap_latex(self.generate_labeled_tex_table()) + + +class Header(BaseElement): + def __init__(self, header, underline): + super().__init__() + self.header = header + self.underline = underline + + def to_rst(self): + return [self.header, self.underline] + + +class Figure(BaseElement): + def __init__(self, label: Label, figure_lines: list[str]): + super().__init__() + self.label = label + self._parse_figure_lines(figure_lines) + + def _parse_figure_lines(self, figure_lines): + line_idx = 0 + self.figure_path_match = re.match( + r'.. figure:: (.*)', figure_lines[line_idx] + ) + + line_idx += 2 + caption = [] + while line_idx < len(figure_lines) and figure_lines[line_idx].strip(): + caption.append(figure_lines[line_idx].strip()) + line_idx += 1 + self.caption = ' '.join(caption) + + def generate_labeled_tex_figure(self): + """ + Generate raw LaTeX figure + """ + output = [] + output.append('\\begin{figure}[htbp]') + output.append('\\centering') + if self.figure_path_match: + match = self.figure_path_match.group(1) + output.append( + '\\includegraphics[' + + 'width=\\textwidth, keepaspectratio' + + ']{' + + f'{match}' + + '}' + ) + output.append(f'\\caption{{{self.caption}}}') + if self.label: + output.extend(self.label.to_latex()) + output.append('\\end{figure}') + return output + + def to_rst(self): + return wrap_latex(self.generate_labeled_tex_figure()) + + +def extract_element_with_linebreaks(input_lines, start_idx, prefixes): + """ + Helper function to extract the lines of an element containing line breaks + by defining prefixes which define the element/block. + """ + element_lines = [input_lines[start_idx]] + line_idx = start_idx + 1 + while line_idx < len(input_lines) and ( + input_lines[line_idx] == '' + or any( + re.match(f'^[\t ]*{prefix}', input_lines[line_idx]) + for prefix in prefixes + ) + ): + element_lines.append(input_lines[line_idx]) + line_idx += 1 + return element_lines, line_idx + + +def parse_document(input_lines): + """ + Parse the rst document into a list of internal elements deriving from + BaseElement. + """ + output_doc = [] + current_label = None + elem_before_label = None + current_paragraph = [] + line_idx = 0 + while line_idx < len(input_lines): + line = input_lines[line_idx].rstrip() + if line.startswith('.. _'): + current_label = Label(line) + elem_before_label = output_doc[-1] + line_idx += 1 + elif line_idx < len(input_lines) - 1 and is_header( + input_lines[line_idx + 1] + ): + header_line = input_lines[line_idx + 1].rstrip() + output_doc.append(Header(line, header_line)) + if current_label: + output_doc.append(current_label) + current_label = None + line_idx += 2 + elif line.startswith('.. csv-table::'): + table_lines, line_idx = extract_element_with_linebreaks( + input_lines, line_idx, [' ', '\\.\\. \\['] + ) + output_doc.append(CSVTable(current_label, table_lines)) + current_label = None + elif line.startswith('.. figure::'): + figure_lines, line_idx = extract_element_with_linebreaks( + input_lines, line_idx, [' '] + ) + output_doc.append(Figure(current_label, figure_lines)) + current_label = None + elif current_label and line.startswith('- '): + list_lines, line_idx = extract_element_with_linebreaks( + input_lines, line_idx, ['- ', ' '] + ) + output_doc.append(Paragraph(list_lines)) + if current_label: + output_doc.append(current_label) + current_label = None + elif current_label and line.startswith('.. versionadded::'): + block_lines, line_idx = extract_element_with_linebreaks( + input_lines, line_idx, [' '] + ) + output_doc.append(Paragraph(block_lines)) + if current_label: + output_doc.append(current_label) + current_label = None + elif line: # Non-blank line + current_paragraph.append(line) + line_idx += 1 + elif not line: # Blank line + if current_paragraph: + output_doc.append(Paragraph(current_paragraph)) + current_paragraph = [] + if current_label and elem_before_label != output_doc[-1]: + output_doc.append(current_label) + current_label = None + line_idx += 1 + + return output_doc + + +def serialize_to_rst(elements): + """ + Serialize the internal representation to rst. + """ + output_lines = [] + for element in elements: + output_lines.extend(element.to_rst()) + output_lines.append('') + return output_lines + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='Preprocess rst files to assist pandoc in creating a11y ' + + 'PDFs' + ) + + parser.add_argument( + 'input_filepath', help='Path to the input file for processing.' + ) + + parser.add_argument( + 'output_filepath', help='Path to the output file for saving results.' + ) + + parser.add_argument( + 'app_name', help='Replace |AppName| with the provided value.' + ) + + parser.add_argument( + 'version', help='Replace |version| with the provided value.' + ) + + parser.add_argument( + '-t', + '--translate', + type=str, + default='', + help='Translate file to given language if .po files can be found.', + ) + + if len(sys.argv) == 1: + parser.print_help(sys.stderr) + sys.exit(1) + + args = parser.parse_args() + + lines = flatten_files(args.input_filepath, 0, args.translate) + + lines = replace_placeholder(lines, 'AppName', args.app_name) + lines = replace_placeholder(lines, 'version', args.version) + + lines = inline_toc_header(lines) + + elements = parse_document(lines) + + rst_lines = serialize_to_rst(elements) + + try: + with open(args.output_filepath, 'w', encoding='utf-8') as outfile: + outfile.write('\n'.join(rst_lines)) + except Exception as e: + print(f'Error writing to output file: {e}') + sys.exit(1) + + sys.exit(0) diff -Nru ausweisapp2-2.3.1/cmake/pandoc/template.tex ausweisapp2-2.4.0/cmake/pandoc/template.tex --- ausweisapp2-2.3.1/cmake/pandoc/template.tex 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/pandoc/template.tex 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,221 @@ +$passoptions.latex()$ +\DocumentMetadata{ + lang = $language_iso639$, + pdfversion = 1.7, + pdfstandard = ua-1, + testphase = latest +} + +\documentclass[]{$documentclass$} +\usepackage{etoolbox} +\usepackage[table]{xcolor} +\usepackage[margin=3cm, footskip=1cm]{geometry} +\usepackage{amsmath,amssymb} +% color section titles +\usepackage{sectsty} +\definecolor{section_title_color}{RGB}{32,67,92} +\sectionfont{\color{section_title_color}\sffamily\bfseries} +\subsectionfont{\color{section_title_color}\sffamily} +\subsubsectionfont{\color{section_title_color}\sffamily\bfseries} +% Table colors +\definecolor{table_header_color}{RGB}{219,219,219} +\definecolor{table_even_row_color}{RGB}{250,250,250} +\definecolor{table_odd_row_color}{RGB}{235,235,235} +% Governikus imports +\usepackage{pdflscape} +\usepackage{longtable} +% pandoc imports +\usepackage{fancyvrb} +\usepackage{tcolorbox} +\newcommand{\VerbBar}{|} +\newcommand{\VERB}{\Verb[commandchars=\\\{\}]} +\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\{\}} +\definecolor{code_box_background}{RGB}{242, 242, 242} +\newenvironment{Shaded}{ + \begin{tcolorbox}[ + colback=code_box_background, + colframe=black, + boxsep=5pt, + arc=4pt, + boxrule=0.5pt, + verbatim + ] +}{ + \end{tcolorbox} +} +\newcommand{\AlertTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}} +\newcommand{\AnnotationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}} +\newcommand{\AttributeTok}[1]{\textcolor[rgb]{0.49,0.56,0.16}{#1}} +\newcommand{\BaseNTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}} +\newcommand{\BuiltInTok}[1]{\textcolor[rgb]{0.00,0.50,0.00}{#1}} +\newcommand{\CharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}} +\newcommand{\CommentTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textit{#1}}} +\newcommand{\CommentVarTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}} +\newcommand{\ConstantTok}[1]{\textcolor[rgb]{0.53,0.00,0.00}{#1}} +\newcommand{\ControlFlowTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}} +\newcommand{\DataTypeTok}[1]{\textcolor[rgb]{0.56,0.13,0.00}{#1}} +\newcommand{\DecValTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}} +\newcommand{\DocumentationTok}[1]{\textcolor[rgb]{0.73,0.13,0.13}{\textit{#1}}} +\newcommand{\ErrorTok}[1]{\textcolor[rgb]{1.00,0.00,0.00}{\textbf{#1}}} +\newcommand{\ExtensionTok}[1]{#1} +\newcommand{\FloatTok}[1]{\textcolor[rgb]{0.25,0.63,0.44}{#1}} +\newcommand{\FunctionTok}[1]{\textcolor[rgb]{0.02,0.16,0.49}{#1}} +\newcommand{\ImportTok}[1]{\textcolor[rgb]{0.00,0.50,0.00}{\textbf{#1}}} +\newcommand{\InformationTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}} +\newcommand{\KeywordTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{\textbf{#1}}} +\newcommand{\NormalTok}[1]{#1} +\newcommand{\OperatorTok}[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}} +\newcommand{\OtherTok}[1]{\textcolor[rgb]{0.00,0.44,0.13}{#1}} +\newcommand{\PreprocessorTok}[1]{\textcolor[rgb]{0.74,0.48,0.00}{#1}} +\newcommand{\RegionMarkerTok}[1]{#1} +\newcommand{\SpecialCharTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}} +\newcommand{\SpecialStringTok}[1]{\textcolor[rgb]{0.73,0.40,0.53}{#1}} +\newcommand{\StringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}} +\newcommand{\VariableTok}[1]{\textcolor[rgb]{0.10,0.09,0.49}{#1}} +\newcommand{\VerbatimStringTok}[1]{\textcolor[rgb]{0.25,0.44,0.63}{#1}} +\newcommand{\WarningTok}[1]{\textcolor[rgb]{0.38,0.63,0.69}{\textbf{\textit{#1}}}} +\usepackage{longtable,booktabs,array} +% Adjust padding of booktabs toprule, midrule and bottomrule to remove gaps +% between colored rows in tables and the rules +\aboverulesep = 0pt +\belowrulesep = 0pt + +\usepackage{calc} % for calculating minipage widths +\makeatletter +\patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{} +\makeatother +\IfFileExists{footnotehyper.sty}{\usepackage{footnotehyper}}{\usepackage{footnote}} +\makesavenoteenv{longtable} +\setlength{\emergencystretch}{3em} % prevent overfull lines +\providecommand{\tightlist}{% + \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}} +\usepackage{bookmark} +\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available +\urlstyle{same} +% Metadata +\usepackage{caption} +\usepackage{hyperref} +\hypersetup{ + pdfauthor={$pdfauthor$}, + pdftitle={$app_name$}, + pdfsubject={$pdfsubject$}, + pdfkeywords={$pdfkeywords$}, + pdfproducer={LaTeX}, + pdfcreator={LaTeX via pandoc}, + hidelinks, + colorlinks=true, + linkcolor=section_title_color, +} +% Boxes +\definecolor{boxes_background}{RGB}{247, 247, 247} +\definecolor{boxes_frame}{RGB}{134, 152, 155} +\definecolor{important_box_background}{RGB}{248, 228, 210} +\definecolor{see_also_box_background}{RGB}{219, 239, 230} +\definecolor{note_box_background}{RGB}{208, 222, 250} +\newenvironment{ImportantBox}{ + \begin{tcolorbox}[ + colback=boxes_background, + colbacktitle=important_box_background, + colframe=boxes_frame, + coltitle=black, + boxsep=5pt, + arc=1pt, + boxrule=0.5pt, + parbox=false, + title=Important + ] +}{ + \end{tcolorbox} +} +\newenvironment{SeeAlsoBox}{ + \begin{tcolorbox}[ + colback=boxes_background, + colbacktitle=see_also_box_background, + colframe=boxes_frame, + coltitle=black, + boxsep=5pt, + boxrule=0.5pt, + parbox=false, + title=See also + ] +}{ + \end{tcolorbox} +}\newenvironment{NoteBox}{ + \begin{tcolorbox}[ + colback=boxes_background, + colbacktitle=note_box_background, + colframe=boxes_frame, + coltitle=black, + boxsep=5pt, + boxrule=0.5pt, + parbox=false, + title=Note + ] +}{ + \end{tcolorbox} +} +% Footnote numbering +\newcounter{originalfootnotevalue} +\setcounter{secnumdepth}{3} % Numbering depth +\setcounter{tocdepth}{$tocdepth$} % ToC depth + + +% Common includes +\makeatletter +\IfFileExists{parskip.sty}{% + \usepackage{parskip} +}{% else + \setlength{\parindent}{0pt} + \setlength{\parskip}{6pt plus 2pt minus 1pt}} +\makeatother + + +$fonts.latex()$ +$font-settings.latex()$ +$for(header-includes)$ +$header-includes$ +$endfor$ +$after-header-includes.latex()$ + +% Language +\usepackage[english, ngerman, shorthands=off]{babel} +\usepackage[autostyle,german=quotes]{csquotes} % Needed for correct german quotes from ""-quotes + +\setlocalecaption{english}{continues}{(continues on following page)} +\setlocalecaption{ngerman}{continues}{(Fortsetzung auf folgender Seite)} +\setlocalecaption{english}{continued}{(continued from previous page)} +\setlocalecaption{ngerman}{continued}{(Fortsetzung von vorheriger Seite)} + +% Fonts +\usepackage{tgtermes} +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} + +% Titlepage +\title{$app_name$ \linebreak $pdfsubject$} + +\makeatletter +\providecommand{\subtitle}[1]{% add subtitle to \maketitle +\apptocmd{\@title}{\par {\large #1 \par}}{}{} +} +\makeatother +\subtitle{$subtitle$} + +\author{$author$} +\date{$date$} + +% Redefine \tableofcontents to not include a title +\makeatletter +\renewcommand\tableofcontents{% + \@starttoc{toc}% +} +\makeatother + +\begin{document} +\selectlanguage{$language_babel$} +\thispagestyle{empty} +\maketitle + +$body$ + +\end{document} diff -Nru ausweisapp2-2.3.1/cmake/prepare_sonarqube_env.cmake ausweisapp2-2.4.0/cmake/prepare_sonarqube_env.cmake --- ausweisapp2-2.3.1/cmake/prepare_sonarqube_env.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/cmake/prepare_sonarqube_env.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -cmake_minimum_required(VERSION 3.14.0) - -include(FetchContent) - -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}") - -set(BUILDWRAPPER_ZIP_NAME build-wrapper-linux-x86.zip) -set(BUILDWRAPPER_URL https://sonar.govkg.de/static/cpp/${BUILDWRAPPER_ZIP_NAME}) - -set(SONARSCANNERCLI_VERSION 7.0.2.4839-linux-x64) # https://binaries.sonarsource.com/?prefix=Distribution/sonar-scanner-cli/ -set(SONARSCANNERCLI_ZIP_NAME sonar-scanner-cli-${SONARSCANNERCLI_VERSION}.zip) -set(SONARSCANNERCLI_URL https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/${SONARSCANNERCLI_ZIP_NAME}) -set(SONARSCANNERCLI_HASH 87f80a41ad861c3d0eb2c00a1268e77ab5f93b83c17c816318ddecb0911baeb0) - -set(DEPENDENCYCHECK_VERSION 11.1.1) # https://github.com/jeremylong/DependencyCheck/releases -set(DEPENDENCYCHECK_ZIP_NAME dependency-check-${DEPENDENCYCHECK_VERSION}-release.zip) -set(DEPENDENCYCHECK_URL https://github.com/jeremylong/DependencyCheck/releases/download/v${DEPENDENCYCHECK_VERSION}/${DEPENDENCYCHECK_ZIP_NAME}) -set(DEPENDENCYCHECK_HASH 16f1c9de836a4e7353646d839cce36f0cfaf8db8d834b9eff9976b0854c9c423) - -set(MARIADB_CONNECTOR_VERSION 3.5.1) -set(MARIADB_CONNECTOR_ZIP_NAME mariadb-java-client-${MARIADB_CONNECTOR_VERSION}.jar) -set(MARIADB_CONNECTOR_URL https://downloads.mariadb.com/Connectors/java/connector-java-${MARIADB_CONNECTOR_VERSION}/${MARIADB_CONNECTOR_ZIP_NAME}) -set(MARIADB_CONNECTOR_HASH 50a50c4a3c13c30dfbd40587f7ad9a496197d285ede0948641d9eee68fdf2106) - -set(SONARQUBETOOLS_DIR ${CMAKE_BINARY_DIR}/sonarqubetools) - -file(MAKE_DIRECTORY ${SONARQUBETOOLS_DIR}) - -function(DOWNLOAD_AND_EXTRACT NAME URL ZIP_NAME HASH EXTRACT_DIR RESULT_DIR EXTRACT) - if(EXISTS "${SONARQUBETOOLS_DIR}/${RESULT_DIR}" AND EXTRACT) - message(STATUS "Removing previous ${NAME}") - file(REMOVE_RECURSE ${SONARQUBETOOLS_DIR}/${RESULT_DIR}) - endif() - - if(NOT EXISTS "${PACKAGES_DIR}/${ZIP_NAME}" OR NOT HASH) - message(STATUS "Download ${NAME}: ${ZIP_NAME}") - file(DOWNLOAD ${URL} ${PACKAGES_DIR}/${ZIP_NAME}) - endif() - - if(HASH) - file(SHA256 ${PACKAGES_DIR}/${ZIP_NAME} FILE_HASH) - if(NOT "${FILE_HASH}" STREQUAL "${HASH}") - message(FATAL_ERROR "${NAME} hash does not match! Current: ${FILE_HASH}, expected: ${HASH}") - endif() - endif() - - message(STATUS "Install ${NAME}: ${ZIP_NAME}") - if(EXTRACT) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${PACKAGES_DIR}/${ZIP_NAME} WORKING_DIRECTORY ${SONARQUBETOOLS_DIR}) - if(NOT "${EXTRACT_DIR}" STREQUAL "${RESULT_DIR}") - file(RENAME ${SONARQUBETOOLS_DIR}/${EXTRACT_DIR} ${SONARQUBETOOLS_DIR}/${RESULT_DIR}) - endif() - else() - file(COPY ${PACKAGES_DIR}/${MARIADB_CONNECTOR_ZIP_NAME} DESTINATION ${SONARQUBETOOLS_DIR}/${RESULT_DIR}) - endif() -endfunction() - -DOWNLOAD_AND_EXTRACT("Build Wrapper" ${BUILDWRAPPER_URL} ${BUILDWRAPPER_ZIP_NAME} FALSE "build-wrapper-linux-x86" "sonar-build-wrapper" TRUE) -file(CREATE_LINK - ${SONARQUBETOOLS_DIR}/sonar-build-wrapper/libinterceptor-x86_64.so - ${SONARQUBETOOLS_DIR}/sonar-build-wrapper/libinterceptor-haswell.so - SYMBOLIC -) - -DOWNLOAD_AND_EXTRACT("SonarScanner" ${SONARSCANNERCLI_URL} ${SONARSCANNERCLI_ZIP_NAME} ${SONARSCANNERCLI_HASH} "sonar-scanner-${SONARSCANNERCLI_VERSION}" "sonar-scanner" TRUE) - -DOWNLOAD_AND_EXTRACT("Dependency Check" ${DEPENDENCYCHECK_URL} ${DEPENDENCYCHECK_ZIP_NAME} ${DEPENDENCYCHECK_HASH} "dependency-check" "dependency-check" TRUE) - -DOWNLOAD_AND_EXTRACT("Dependency Check MariaDB Connector" ${MARIADB_CONNECTOR_URL} ${MARIADB_CONNECTOR_ZIP_NAME} ${MARIADB_CONNECTOR_HASH} "dependency-check/lib/" "dependency-check/lib/" FALSE) diff -Nru ausweisapp2-2.3.1/debian/changelog ausweisapp2-2.4.0/debian/changelog --- ausweisapp2-2.3.1/debian/changelog 2025-03-18 14:26:23.000000000 +0000 +++ ausweisapp2-2.4.0/debian/changelog 2025-12-05 13:11:16.000000000 +0000 @@ -1,3 +1,36 @@ +ausweisapp2 (2.4.0-2~bpo13+1) trixie-backports; urgency=medium + + * Rebuild for trixie-backports. + + -- John Paul Adrian Glaubitz Fri, 05 Dec 2025 14:11:16 +0100 + +ausweisapp2 (2.4.0-2) unstable; urgency=medium + + * Fix missing runtime dependencies (Closes: #1120853) + - Add qml6-module-qtcore to Depends for ausweisapp package + - Add qml6-module-qtquick-dialogs to Depends for ausweisapp package + + -- John Paul Adrian Glaubitz Sat, 22 Nov 2025 11:34:25 +0100 + +ausweisapp2 (2.4.0-1) unstable; urgency=medium + + * New upstream version 2.4.0 + + -- John Paul Adrian Glaubitz Tue, 04 Nov 2025 14:01:42 +0100 + +ausweisapp2 (2.3.2-1~bpo13+1) trixie-backports; urgency=medium + + * Rebuild for trixie-backports. + + -- John Paul Adrian Glaubitz Sat, 20 Sep 2025 11:10:54 +0200 + +ausweisapp2 (2.3.2-1) unstable; urgency=medium + + * New upstream release + * Update Standards-Version to 4.7.2 in debian/control + + -- John Paul Adrian Glaubitz Tue, 08 Jul 2025 13:31:19 +0200 + ausweisapp2 (2.3.1-1) unstable; urgency=medium * New upstream release diff -Nru ausweisapp2-2.3.1/debian/control ausweisapp2-2.4.0/debian/control --- ausweisapp2-2.3.1/debian/control 2024-10-13 10:46:45.000000000 +0000 +++ ausweisapp2-2.4.0/debian/control 2025-12-05 13:09:30.000000000 +0000 @@ -17,7 +17,7 @@ qt6-svg-dev, qt6-websockets-dev, qt6-tools-dev -Standards-Version: 4.6.1 +Standards-Version: 4.7.2 Homepage: https://www.ausweisapp.bund.de Vcs-Browser: https://salsa.debian.org/debian/ausweisapp2 Vcs-Git: https://salsa.debian.org/debian/ausweisapp2.git @@ -28,11 +28,13 @@ Architecture: any Depends: libqt6svg6, qml6-module-qt-labs-platform, + qml6-module-qtcore, qml6-module-qtqml, qml6-module-qtqml-models, qml6-module-qtqml-statemachine, qml6-module-qtqml-workerscript, qml6-module-qtquick-controls, + qml6-module-qtquick-dialogs, qml6-module-qtquick-effects, qml6-module-qtquick-layouts, qml6-module-qtquick-templates, diff -Nru ausweisapp2-2.3.1/docs/CMakeLists.txt ausweisapp2-2.4.0/docs/CMakeLists.txt --- ausweisapp2-2.3.1/docs/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -1,11 +1,33 @@ if(SPHINX_FOUND) - SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/releasenotes" "notes" BUILDER singlehtml html latex text) + if(NOT PANDOC_BIN) + set(BUILD_LATEX "latex") + endif() - SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/sdk" "sdk" BUILDER html latex DEFAULT_LANG en) + SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/releasenotes" "notes" BUILDER html text ${BUILD_LATEX}) - SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/installation" "installation_integration" BUILDER changes html latex) + SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/sdk" "sdk" BUILDER html ${BUILD_LATEX} DEFAULT_LANG en) - SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/failurecodes" "failurecodes" BUILDER changes html latex DEFAULT_LANG en) + SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/installation_de" "installation_integration_de" BUILDER changes html ${BUILD_LATEX} DEFAULT_LANG de) + + SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/installation_en" "installation_integration_en" BUILDER changes html ${BUILD_LATEX} DEFAULT_LANG en) + + SPHINX_GEN("${CMAKE_CURRENT_SOURCE_DIR}/failurecodes" "failurecodes" BUILDER changes html ${BUILD_LATEX} DEFAULT_LANG en) +else() + message(STATUS "No sphinx documentation will be generated") +endif() + +if(PANDOC_BIN) + PANDOC_RST_TO_PDF("${CMAKE_CURRENT_SOURCE_DIR}/releasenotes" "notes" OUTPUT_NAME "ReleaseNotes") + + PANDOC_RST_TO_PDF("${CMAKE_CURRENT_SOURCE_DIR}/sdk" "sdk" CUSTOM_PANDOC_PARAM -f rst-auto_identifiers OUTPUT_NAME "SDK") + + PANDOC_RST_TO_PDF("${CMAKE_CURRENT_SOURCE_DIR}/installation_en" "installation_integration_en" OUTPUT_NAME "NetInstallation_Integration_en") + + PANDOC_RST_TO_PDF("${CMAKE_CURRENT_SOURCE_DIR}/installation_de" "installation_integration_de" OUTPUT_NAME "NetInstallation_Integration_de") + + PANDOC_RST_TO_PDF("${CMAKE_CURRENT_SOURCE_DIR}/failurecodes" "failurecodes_en" OUTPUT_NAME "Failure-Codes-en") + + PANDOC_RST_TO_PDF("${CMAKE_CURRENT_SOURCE_DIR}/failurecodes" "failurecodes_de" OUTPUT_NAME "Failure-Codes-de" TRANSLATE "de") else() - message(STATUS "No documentation will be generated") + message(STATUS "No PDF documentation will be generated") endif() diff -Nru ausweisapp2-2.3.1/docs/failurecodes/failurecodes.rst ausweisapp2-2.4.0/docs/failurecodes/failurecodes.rst --- ausweisapp2-2.3.1/docs/failurecodes/failurecodes.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/failurecodes/failurecodes.rst 2025-10-30 10:10:48.000000000 +0000 @@ -1,3 +1,5 @@ +.. _failurecodes: + Failure Codes ============= The |AppName| will send failure codes indicating what went wrong and where it happened as well as @@ -63,6 +65,8 @@ Codes ----- +List of possible failure codes: + - | **User_Cancelled** | The user canceled the workflow. In the SDK case, the user can also be a third-party application that has disconnected from the SDK. diff -Nru ausweisapp2-2.3.1/docs/failurecodes/locales/de/LC_MESSAGES/failurecodes.po ausweisapp2-2.4.0/docs/failurecodes/locales/de/LC_MESSAGES/failurecodes.po --- ausweisapp2-2.3.1/docs/failurecodes/locales/de/LC_MESSAGES/failurecodes.po 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/failurecodes/locales/de/LC_MESSAGES/failurecodes.po 2025-10-30 10:10:48.000000000 +0000 @@ -140,6 +140,9 @@ msgid "Codes" msgstr "" +msgid "List of possible failure codes:" +msgstr "Liste der möglichen Fehlercodes:" + msgid "**User_Cancelled**" msgstr "" diff -Nru ausweisapp2-2.3.1/docs/failurecodes/metadata.yaml.in ausweisapp2-2.4.0/docs/failurecodes/metadata.yaml.in --- ausweisapp2-2.3.1/docs/failurecodes/metadata.yaml.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/failurecodes/metadata.yaml.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,10 @@ +pdfauthor: @VENDOR@ +app_name: @PROJECT_NAME@ +pdfsubject: Failure_Codes +pdfkeywords: failure, codes, sdk, api +author: @VENDOR@ +documenttitle: @PROJECT_NAME@ Failure_Codes +subtitle: @VERSION_DVCS@ +language_iso639: en +language_babel: english +tocdepth: 3 diff -Nru ausweisapp2-2.3.1/docs/failurecodes/metadata_de.yaml.in ausweisapp2-2.4.0/docs/failurecodes/metadata_de.yaml.in --- ausweisapp2-2.3.1/docs/failurecodes/metadata_de.yaml.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/failurecodes/metadata_de.yaml.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,10 @@ +pdfauthor: @VENDOR@ +app_name: @PROJECT_NAME@ +pdfsubject: Fehlercodes +pdfkeywords: failure, codes, sdk, api +author: @VENDOR@ +documenttitle: @PROJECT_NAME@ Fehlercodes +subtitle: @VERSION_DVCS@ +language_iso639: de +language_babel: ngerman +tocdepth: 3 diff -Nru ausweisapp2-2.3.1/docs/installation/CommunicationModel_de.graphml ausweisapp2-2.4.0/docs/installation/CommunicationModel_de.graphml --- ausweisapp2-2.3.1/docs/installation/CommunicationModel_de.graphml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation/CommunicationModel_de.graphml 1970-01-01 00:00:00.000000000 +0000 @@ -1,228 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Internet - - - - - - - - - - - - - - - - - - - Lokales Netz - - - - - - - - - - - Lokaler Rechner - - - - - - - - - - - AusweisApp - - - - - - - - - - - eID-Server - - - - - - - - - - - Anbieter - - - - - - - - - - - Browser - - - - - - - - - - - Update-Server - - - - - - - - - - - Mobiles Endgerät - - - - - - - - - - - Drittanwendung - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eID1 - - - - - - - - - - - eID3 - - - - - - - - - - - - - SaK1, -SaK2 - - - - - - - - - - - Update - - - - - - - - - - - eID2 - - - - - - - - - - - eID-SDK - - - - - - - - - diff -Nru ausweisapp2-2.3.1/docs/installation/CommunicationModel_de.pdf ausweisapp2-2.4.0/docs/installation/CommunicationModel_de.pdf --- ausweisapp2-2.3.1/docs/installation/CommunicationModel_de.pdf 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation/CommunicationModel_de.pdf 1970-01-01 00:00:00.000000000 +0000 @@ -1,463 +0,0 @@ -%PDF-1.4 -%âãÏÓ -1 0 obj - << - /Title () - /Author () - /Subject () - /Keywords () - /Creator (yExport 1.5) - /Producer (org.freehep.graphicsio.pdf.YPDFGraphics2D 1.5) - /CreationDate (D:20230821114836+02'00') - /ModDate (D:20230821114836+02'00') - /Trapped /False - >> -endobj -2 0 obj - << - /Type /Catalog - /Pages 3 0 R - /ViewerPreferences 4 0 R - /OpenAction [5 0 R /Fit] - >> -endobj -4 0 obj - << - /FitWindow true - /CenterWindow false - >> -endobj -5 0 obj - << - /Parent 3 0 R - /Type /Page - /Contents 6 0 R - >> -endobj -6 0 obj - << - /Length 7 0 R - /Filter [/ASCII85Decode /FlateDecode] - >> -stream -Gb"07bDp/UYoPgMYK.en[(d\MN>cK'#OFP!\.O6jK*XbErVo.[>0G-;T87#T"Vr`oJK.X0L$u@W5KrNV -B'/n@ScAX^0E:qr^VEn)q*.+$5C`V.O4OV$J$b)G*Ig&WT7$Q]IDs6fhgYLd+91`@kPti3sg.jZdR\^hAXFL8i -;_?8'h1+S)l0A77m=05)Vk="@TtAkdqS.O0\"%4s07Fk>JBihogm1+>>Mj1rlPm:q0eEK#q07PNq,Zdt -9[:+-c:i]+l`J\u%UO'rp2o#8X6Jerb9c>(d[p&C7^&;VpE'Y7hB/kHUUAOOrCHfZ -j$%N]Vk`u.VaGk@a*:#SlM..!gZcU*m6#1ZYnDgmpXo8aD!S1@_3f8\)/I=Vqas,%EudokgC>k-r40K- -`+O/V5\IY0SOrBbJJ\=?0cTefVYptEMVI`0n\@gpIeC0jB(PYDQ?i"KI[m"=DtncWc[WnVJ6F&iN+SSg -oL#8jBWX)!nQ%s%6LT/CgZd*H%[R5XDa%iOcH8abcOpJJN43+Rs'ot0?##8MIp&98ZWcHarLn=,efJJ3 -b3>MbXI=X)r&/3^k79&=hd;`B'*+H[3^#o^\)@j`iQcG%_7oQphpJ%Z0VTB7USQ -Q+kUK'BXSi7^NM'4b7UFBD5gPt#PHMN!ZeXo]+X>kVKioC -5R5gt>QPcM$"Q"bFl&C-reZOh'LE6,BoJ/#F4,K"?N=HCIP;tKLm;U[u`k0 -+"Pa]>NtPG[!)/q9$Yo>i^nB3_\ -V]E.a\id)mj>p);&BE81:n.L:9X3sY''Tj"=T;I/"f/Z5ra0SX.%cMXM)T6%f2AO'q7d -I),aNa*RC>/Jr0s3`,^kHMu,BrpVk$qF;"o(e?s55OVPf%HRE9Ii\=UpZg%j@K#&7``L;KJ#pJY -3s=)2P`BY=rbR5e.MeY:?OZiW]>*@C3l(,kY*WJCGPj-^Mi]4o.Ld?eIF'[= -lPkSA=(p)'PA39\I[FLi/97R+-WPBNA0V*4D%/<;[VhFX,a," -3R%4MZS]o#G3!J@i'F3/@tl<_4YULlHBj?A2-7DoQ,_/7IMr`];UMhh7hIqR^d1=<1=+>:Q!pQh<\;VR3r/j=>W08?5&`\O&2af4@6fb?a"d(qTiBK\-pe"gc$np>N3]GqIO -1!JuGm;6I8B>&5r\^AWbEB:%g?ISQq5W*`Xl!`t;^HB\ad%FNg5aLZ'q!?^mL+RYs&mmA(A*(&X:rb>c -b3$4\We/'WUl74+,pra*J%J0uZu3FGn5"TafX9"0b9Mctkm>PZn'D;^(A2?N0WmnoA`uKW&,M`-ShB]P -.(PT<6FZEXA?6g\MEsasiUd>o:58jp.<&J-WGKUf'mf";)20IT;AJOJ;VA-.c#VfYGIUlf#<\LQ,3\WG -[`]hX2X,A*D1ru?C992;J*1m?l6)egPukn:`1S'h!nVu\cK&,]G6244,i*-kiDOnJh%j^D?O%#bD]gr@0?tM&U[n\GDhF< -1R;8e7PNu9L(SfF1SldW#G2GMR)X:,K*KRB)Sm.`KR&PbR.P[8&UY.+-8U5_Kb7#VE9*QRdC=J[21$1V -SX[sLg^;`>Xq=nd7@gnSFtYe+IB=P[nG1L'f*FRdA#G"mCtMHb\A*?W\`7crN*\MeqJC)6$3&,7n%?hh -@[`Ds_3o0p2oFQ]O9@TdF@3gk?UmK@%0)c_9_BUd!^VGtm4]:FSK>)(%YqT4CN\;m2j$]%UV>dA6].R+[)gj%q3rEqSF2S>cI,LYh -aa_OlJW]+@$.?)mE.K8QjS^b@I'n^@'D(^X+IJ!'H6?Gpe-\!+nTO[3hZGe>*=IRidF"PK*!O-T*7M0# -#T%>9tl,]7sDT/E/CI^!8FL+6(>nu%:07?o>D+XSWXB@iNm&]RS"NB\\$o88;Jf5G1@K -4sS>5IH:>/$X,1O+)jeakP`;hg#W*dK(fAjJZDp:]qC;TJY?Zomm"M&6&TaW!MZW]pE/aF#?oGa9)et] -pU.TF+/:Lfo)=1\P:uA`C`FGTVmDcG*OCsljo#9O@(0-jMZ<8/48eYq;Z?KtNb+^rc5b"9KEH<)nu6SY -*aH\?@5nZWG*E*YOZPf;LVn*O*P/[t3:%u=r[SH`k^Xnl?O*Ogqu!=?obHBQl1qMB*2&>R!'P#(k*##R -RB<@IfN=gNW-+s'%T%OS?46"NNd^*M\#/g=lfb"qj1+c)D6R2"Ke+PmEa5iA#]XP5feKKUU\H@R+VAa= -J&V0X/5@5SmsZhdMD$(J?dV<3qq6S)s7!Y8pcH0BB*%`OKEnZaZeRJ`a8So>dZ&]Aro,I-dpfVsodJik -kkP8V_]S3lQa^@qlg$h'&+0$O^\G_(MH2ddot98V+&)[;rm%4k5UJPkH)Gt'+Q'*rlbK.-2hHDtf'b2( -HtE6`s%Z8=+!\%?e3uH,m/G3L9$tCY[/Y0orl[b-0,O#j>dXNOdJhInLsPEJ*MhtWd: -)>Y(&:(_i0?JHAC -oPeo5]sRc/m%rZR`8;p;hAHp>/i?5r-\g7(E'N*3mK4dV*-D1+:_QpoL['q&m#Zh^=[]@#T8H?=@deau4bX+FSo&0YBGXG*H0h74*/]24k -[6TIeOc(7IYSD(7/8tdOG>KT^n^Gta7PRH_iK9>'=_%"Waf2\ -&\OBhF,7q8U*#HB!cQ1mrfWf*hch0-+Tm([.uFE:o]X;l_s+]Q]&Z]3\@54S@qMP$^k#!Gos"n&*/06U -=N5N^ULKmiCZ8'@o8"gXK3AfXNd5oYG/aoBn&!b(FFf<0*c6)nIn]9V3bB=@l-Gr*LERQC>Kef1n]pr: -geSS9m`tfPI]X,S@O`WcoZ.O:nD;XO[2ckiCuE(bH6c;DmMS"UOJT'9>0;jUKsBcOMRT?Vj5odiKjI;9 ->LmiUJI.)I2X*s3L-ml9aiUV-d_[O^Qar]od_Qj&IILLHg(XCR;A`:+#BfSW0#_mU3AeVnm3Kh^+c8hB$47R"aVo9"*<2Iq0q6VR!j]2#^f@jNsMG9rQq:&@O6o^@nQ -5mM"FBtWRa-g[LLZQi3uLZ_F:[I[MTV"'$GpW/+lLZWC56`e9b0kp!qV6OHm`iGM4Ubk:(9mL)[]O7X+ -5/c\_8TE[4qO:*uk4@qu4c.`Gg[n-9)D<)MZn%+PA6!\bN*31*V6Q_U`k^[ZI@"rg#OX,5+\j-Fc/Ld4 -)RWpd]YNJ-+#HpW6hjhjBoF^)H):%VrfN-3iRh]+q:;QS=e's-p;i"K=LsAn43tP;QS774QT*.W#;7qS -4*DF3"Fe=pK+[ES@gDHpIE)!EDXc33*P:'+F!s23,9r',J"ouF`EJ(B2%+$MkL+qq9BONC\uiB6Xcj'6hNo&7oE`UQ(QjQ5%P+0#&)Vt]T9 -?J"M>UD]d!RfslhIGl-:WIk`!@fGiLMT@2I43oE839^1]TlBk"2e&-aL>Rg%J;a;]-Y`_8o>'AqhP\.N -n`Vl*_(?79%/ondU_%N":[* -ID1>)QJLF8k^c!aMZuA"B+;b&cTYbVrrO9+rWU3<#F^$a;P1#(>J/9V?Z'W?HqoZ/TbN8*4M06KDacuC -&I.7dG)!F#XC:F)8N?M@"cej-XN1@h@;?t8>LZ#F'&4g[^(V6`"1@L2#ijq!mHN*Knrn!,51f_XG4a)K -pA/T2)WSmDOmk=loKXe6R5B8]XcPl:fm46s?-aEQ]UNifRPLo^nJL$f;gI7pZuZ/YXoF.7]>`uaO:c]a -GpN:>KsP@qg,amo+^1?-QL$]P-u![-\@[h3W]gk^R[7#lld/]CRRI'"nfR\oe3"K5m(e\e`'_-Ba%i)` -WBP3JG<<+@kdRsVaq2;^7)R&TGW2b2k,E$SR3U?LfbtBBn8OP!IDWulr,T\)N@o2,iF]m)&j -mF@(M$`)C4b71%]S3IPOj+J*aeXgI-*["bH3)MJeP8tW2W^k\DNk4U@@*u"!qURbl0[?>qWf;FC2D^hNSAil;kJ*hIat6.:Wp_j> -RZ&<^)isWb+56#1iV;GB6o4F=>B>L>5TKZ%7p8WN%qnD(Hi3Hr48a,E;VpTU:744(qZVFFT)BjA2=;Dg -;t+.A`Hn+oW(HUGnquN80"#Sp<`1]1-p6RUA!^9r[,b5p\WB4Z8UJ>9'rn!6Bnjbr-0/&p;"d7_iJ;<' -]W+GPP#mZK?!Q=#Ei.%!bhb@doI\VuPguP#oW02T[I>0!?nQR[LiU?5:p<>[E\tr_$RgYLXC# -$%30FV>`j6o@=ne%Hm7M^N=aTEX;%UY8k0e@d/]1=*X(ssh`g#^aQ_oH83CKC$8H%gBY -%sc,L$kIV`D?$s[d0t;SiYM21YH%++afATN%R;4D,^JKRnd<-*+g7p5@$asO#K\L?bN7&'qa$Kd[^:=d -#7:q@[3en[4If?<4]$+e(Am`fDa;[,li6`KDGQ3h*"RmY1*5h>-RDOSi[PBqE&3F6nqBQ!P7&2e1N7:G -`4ac0i%!Qp1m=W'3$@Wpd:>$SiY:,o4r^WIFXTeW[R-&8mQ?jNM,U5!\nR0i,-6^6Cl?(2!XQQJI=8OX -:c3YH=W`rA&9QE4JCoaR?pK03>a00:&f2cM*?9K@%k[W@k!em?RC+DsESREF]1`<[mgcH>?S4!MS(cWJ -3^qlok[2iER;>:"k9aE&emhd@W&W4]k.Ig%e5]jF6rQDeHICQN&)&]W[*Zpl^14&M*Z##toGSY@=]$/> -#,NHbK%iCk]fVI;@#4&!+13as7eB\2IQ*Q_]<*Ob\2 -(CV(@21GV.!u@+Y4!(A@F/k(Xb0`VYR^FSnKD'@ -q"`Pl*kIUNW68kqsNJtTASUo;'%/l/]+bcrRP'ij;Yg:5\$D@o*rS2(R#j'il,%7 -#$O>$#R.G]_K&2I=2Qe*L3'lH)&XUS!AV[L$O -[+RS/d%>lT-j`JLNf_q/cPf8D)o@Yp*pbE2]rDQ$jAWCUN_gg;P-33^j[`n#S%M`bh*fja6P,4H#`ViO -[;S(V)K]W`UfL#MbgUO,F\VP;B$Gh"j9'8`QV&YJ=^!b>J^M"sQChjS^":,=HrH2h$N#.:F]u^0p`8=e -B:!5&.ok)SC,0m1pC?q3A"Dc$h4,A;5?4YO($tPQg-3O17DiDHe_S7u)Rt%V9t#,YV%,7VfBuE&k.=TR -pL?]WpFA8`bZY&!9P00'-rabN)P;,4Z/t4kc"&A&PdO9i2k@#`fSu>AqDR.ff`R@6`uh:Q=b_U -b(J:;?K7U)9La8ITp^iSQ:PV;;tqg3m$:D>gJ$1[B8$87b/568`?/lY1 -ZV;A8BW"SO4iVY\;4>U#0lQ?BYI&^Za>K/9h4H1AA1%'G8i(HDY%&2T&GcKYZS77'GTtaP(#"20aS`G^^T`<1\G\/O+m' -hIX2em*91?2njLfd+>D-9*`l$?-U\8[-8>BNf_H2,7n5""Mr41Y\?-`b5i>01-!#AW>7f`-X4kZ^=)R[at*H/NkTdAB*nr+q#T^!=U -h[3&0e"2=P^1i3&DM?(\lGV&p5jHI=@S(?br\n"\aD6OS\JM@m$$ -rO3TAFG?Hndi0+*X<7ib#K:^Efd9tii`5Oc6Hr,^Xc*iD/`hmk@:lCq3t#=ATO71X>kl&.%($fCEnUs7 -NC___>8hNpC_Li^O<`8s^>Z(EZKsS17'bpBIq5\&#Q2^'(%nKL)\1Y&\k2\-&tM-Rt0"&Pa"f%tQqbN+J/Dk5BIOhUtP -G5D3ZX9S_faUphj5V/ds.dU.Y3YC2rU/05ue'P)G`CjMGVLSbO\Moc5K!gg`WK7#*ROL6(00Sj5.]e!Z -ee@dp8'7U.-YC7-IlLcU#LS?q)*'](bYE.g[W&j\8Do>3^3iH%;h%_C"g%YMZ0k+*/PM^2WH92CW6"/& -fpIm7#.mO=27RhJdGZh6IDWFf5)Ag6YjW0RrY5[WdjDpAQo!?P+3u_Gj#`:ig%2D7[5jg\odkXI#X1&" -Xep*33Sma._co3tl3ATc<5@]`WO`79+4>'WMGLV]J^WYU05De,?/Dd;jfm68m.)H@[JfqE<@4tVI]Tt_ -n%8=`niF>)kB^(k&DQ:Oe%3mYT6?g.?#D'6\ZkPnan$e8jTcsB-[,=]Wbt8a/t[7Jq`\?YdGA0C:g)kf -dN@YaI$rda?+s.I/9X$cBuM\@]"<>E+Q`$+,cZf)>\`WrK9XR$ICs*_`%GDS"nL5RZAuQPf'9N`1&Ch# -jbp"Wh;uMf26h9[\?gU9:iNBkq.Eu#laeI14[25XF -ZtF.n/-@kB/U)1)?"a6W7S#B%P45rF]=7irDpBM[(Fha8A2JU0DsK)?Z#[;*J(#k(I68$sqrcX8(=.I> -)+st>G)?/@$lAf[=8^CLZi2E0q`R$AV"Bu$V)?/a]9H:YOLZ2C4rlFO)`dB:dMZ6uY$[&1/J8/pOD0Dq -OqLZ/:ggKr`[X>Cfs?DFI)"3(Xf[/[ps$X\W+F\hb[Rd6;iGaogeW8%b.Kc-1!di2k0@#>rGCgM>oBKM -F"9gK_F\ZcjImr1*dUq8N.jk!c0f(ER^r@IVIRkU%1K%;C/uEB`4?;4Arkf8gdukJ8:Qq\\-NCImh+4` -NFcl/R"_&CepCOMV^t9"QT*dAEu#n)%qXOA@sk&FG9;n)O#oZF5LW-$)A;$kI,B#S1j-4`YVfoc<:1g& -c`1%S23!.-TjiGL58p.#S7^6;&o&^$fP9Wf9TGk!L=L(Ng`@.m6s7WYNKhJ%34L=io5i#:&Q+"eRdnEb -J&CB>Kf,EmXgL._/38a$UCn()akq:)T(ZdVO8/!h=?,Y9\ugq42XY;X&+40,_*&[$hQGF3W.?o\3_Te/ -,>K&X;9;@W3D3"r4@`AV7@:A]VPD)(5'iOJN^qnS5OFdC[R9DghTJSb4^00Dko4#kqOPY2/aG.7*J\^` -3o3gh7)']gC&P_DFD"rLl7%&_Sg202S+`pu!0sEoQ$\Nb4hTAt!aE?H\f?SJYt+'(i,aA)IE"VVrD]@R -kb(?n6EkiW-_-mb.q8;*QbGlBFs>bkB#`1Z=#-u6$@`'A_',OZkcm1cR3bf,bB\I,m&rpfn+#%!R@<-k -28$ZlJP2.ZSTT&iq4:sI),'\=(4d1G1U$#i5$rCO7&T4'?OH8Vqh]U(&(a:uqfL!XfEE$h*kS)L-DRc< -7:B9fB,%a_>IN13iPI>O+$][;UiIttH\A#DdEq.'-#g'6E4r.WI!p?l=9&JX\bkFA20_!4f[sa#o -/YOR$^D]:.p?CNpj%P]lRcPjJX]t-QH#tQo[PrOWXf]*I@Mnlt1KAf`>ZhgYc[-kD/+DBnfJA54$f5'S -h2!I%bdaE3PmQJqB2kbG3^[?lPSrLB1Jbbu?g"t%"f"$B'7%nn4*(cXVb:$KGudqO12M>sRlE3ueo#r* -rBSH*\&`tqOLM6a(b==n="9.#Ldi^Nk,OtU"*\..h*T^f6f0eY!B -]=l@'JO_6`'!kYfXff@[d6Htt4J*_hWaW4p3WA7Y.JgmZ -B(d_2[F8t]@=nC"kBH^_VO3.#'uH;][j%Hr/%up'A!2[a>`C+Oc51F+@X`8*`M>_%7h:T[0q7cL>f'S& -C=Mu`j3jE&Va@)^LV:i:eD&,Z(tZ:Is/U,Gs&88aO!!3tC-\<$)8=./`NUGrIDPIO5N,H.:[Hj)Sd6*$ -),cIV\T_IYSDfNe]C*3?N-\epJZ-(5SfVP[,/q;3jukuO_>+BEOt,u6f6\4.K*gL>"s-8""J,%KNQC!M -^JA"tZXOT+dKgPT*sa1Tm9/Ecci,]6*TQH]9XPUkf.f'i*Uan=m-N!oOdS[pRm`0Pi59ueS`;6LOj1kZ -Xc23e>"Ym4dT="4=K.`.`fS.H.:U_&f82]NTeho0GX"(GTnQn^kh6O/4FG1C[.n%i3KtKEVu\If(@YR/ -`W-j]Gj0KXEBaYi7@qsnj"[O-\s&@oS2Jirq.O/mJ\!U2!],pi^XRRjTnrE:FBV,?&#L4V*n[,UQHI=: -FbWm5;H,gVDlqOTq+L@rk(/Y?mkHJa8"M+('?l%*jP/<[.d_V-oAATOp#U$a@>n6`4*jT4Tl0MhR'5Ef -CHZJ;325[g:mniRXn5:$jblNV;5$FcdHKV!H?$@arbRg!m509jQO>1VclHT(>#qhS/mE@Rk$0\=QP>sG -MUk2ik#S&]EQ9PmJ5e>hjDm*6k]mK9=Da;80,t(;4t_-3BQ`\Qo(GK+=SkSDf;Z#dc%XUrEq%e"KtQ"j -&W:Pu^`_Y2R$YaQ4@LRS]3hi*n`)X9\@@;M>C;tQ(X+sdr;Gh^Hhgjnr`3gjDf.,7MhWu3"#K#!CS:>e -A6>TS^-iBR;c8sk(=COjm^t&WbS-'/rCC,l<2@-TNVK*RoP:4UWFpi!VPr>Z/sGaLO+@ -hq@97[9b[AZIptU)$!Z6$;PdB/)=Zn)Tif:PZM`*HUN%-Z#I^>/2*\Wrj\==9b0q[a39FQ50^TdX<'\[ -DQ@c*3nH?7\Q[FcnIUmu>BELR04'#^*@OCN-nYCnh^o_Eet0fS)&U4!#'2`eX?O_AR(p6e8D/% -db\f`ia^OLY1Br?$]Pjn*_>fG]g#^kIA0kmSb;>)l)9u8*#+YQLD-0rIFq>%K=mB>Ce`qfk*'\RP,GhU -fLF!$1f^oo<>pI0)\>;7*&Gb`M]WD2k>GMW[b=IPc;78+&L.60jLG^9&*:Ej-F*3EBf%m(CK#YCL=9h" -E(m!ZdN#sXr`X+(;fL3PZ#_gLQW2h]R6($4R*15Vq-HJuPLb2/9N.G+a+:78&$dT9Wr?g//r::KeFP>t -lQ-!&=%rA_#Q)V+Rk=NXEBHJ8o<1-ff::$u@b@N9)2S3s%jll=c$=dCY+O_bA -2s!q3nUU=F;-V-fZ?H&lHPPeu\+W_Y%N%d`&m=kS^9(O#X=0"D\4&7a@MJ2UbM?Qb.(.*8;>8k*id90U -@(hsA4)c"V_]J6CiNmtKA%\8*aCC'-<)hC#[6,Y,A27+]?g('![X)LFrj\?`(5lXCaam-[rfAX9G+?%1 -:Mg4+[/\?U&Y;V3LUH7<0XL=$PjE\\TR(J,+XWcC3R:N:!3=(**^]W%AQ ->T!]DZ6MfSb]I&nm&!0]>-p^Y2:oVqp;ZI72P*J8O,aIO(_O/#RR$<"CuW1HZE7`&e7J_<`mnbQL15C4 -GL3Zb]>&\&6da1obRL"DYN1%d*+W\p?=eP[)Y*N5>_l'NX(r8f7lf&]2f;2T,]M%VOW-;tMf^Wkp9S:+OanGM]M%B0D.iLKS08 -'kT:-AtOoq5h5M-@aB5bYc;?F8YiG5dHk'Y92gp5]=Vl5mG6US -n"dXt\tcu`pXIL>mbCaeqVFY`e#W61m=#u?h7]Xtf\XT[jO!c`9;2(`ZFrp/eGJsd4?[YqHZr9KC=o)( -KuVHP]=2l^CYb0=gq/WTo"DZUA$9Y?nl,!uXDnfLr)*2^3=W;-nHj6`IffBEVg1i7MN4Z_;SCU`R"1e]6[9b\276Q^Pna -8j=#=c=90E@bKNd5 -T:'NGUSYXl/(OOlV4h!3_tI+-V=j'*?]JO#e-2gb>2=2V3d"JZ.c@X\a#\J?@+Fe[fe\;sD43#iJt!L> -n+ppjPL7M]'HdNHmB3kCZ4pI%4)'+C!?@HQiOFAB<-8Ba=NTEh\J5rG.sQ8tDog?1:)Yc6Rg&UtI5aen -`qj.$X5DL&r_B=&pcR2&!r(gJW9WU?7$kq0^TZ@2XA`H4HaPIFd::M3M>)6FCGt]Wm@pU#k.iJH^+!\K -O%@[PJ+l!A_KGLGPY(K'5!3-=,T -B_!J7[bBJ9S8L_Hn_nj,IHXi:W1.Mdfjlsr'1HE4VWNnY>%#?P:f5Lj9m-'/@i?&]*mU[=Rftls422H!5`rT-hq"[\4_0MV^(9+r)!g,J -akV$e:f`%>hX"C_AcPh&N?pH)MM#*V1Arn3heE$:[_R_Nb,IGYb"bIOA%B58GACff:t*LljI5L(c,HZ- -aZN*;s4O<*oUq>4DJR^&bc'q.9@9g"^55jLh(XI$6^JNqjF!,(Dd#\(CH[Y$UqYT)XZQHj9G=EKaIOAHSZr`cD9; -9qjA"2u`rKp&htH;fhO[c;(&jMoRpXf(,VD\1"$IJ&.BG1EgVMK-?*(V$L1 -:LL_q/#u$VNn(0UXXeZr\]LG>&In+1YF_=+.>iBRk03&&=gbn:@nRkC?Lu?48EAZnE'1eb>M@]%P6iLW -.C2e$@3r>I&-oUDZ_LXB$SeSYoIhXq]D*LYj,,=4P"4quR]hTWKphG!>j_J`[)>=$6oCIi;qT"[=SjPj -1FQb4s44d3?WOLkH#RY33:n5r]PmQ]1X!*/c"tfIH#NSCVJ2:7KB(#(L1I*f%bDM$^5-UY7VqX-.r)YM ->ZV'<+cX#)gU^\*I?']ZGOJR6*7m$VXXG7A(!$7"Ab\R8?LralMsLV!*i@O+6H&8Q^0n'*"g0=@)JH)3 -A)JsM."8crK8^Zhc5i+KQ07R+&]AhLegg&R%UAX5F!J82Ad`]\h)6/!<8$&gI'[3@QMFKQo6k'W!A"X'd:27h)%)H@XMktF]DNk -J&Qj.edlfe:#2b9G/mPiN/og$jh#"*pUmV.Hqc6^(]"c``QDCi\mb"M;1`L>99morMHuqcneXk^>MZ-(!\W)n7aH<%Nd`t9lDAl3LNZ5_MXYhkWn#ss4)=KEXAS5n -]h2=^]6;qec?jn85HH_!"A#\e]bduAHBJFXMlA'q,$@U"gg[i3qN,0aggWT\Q-m;1^Lt5]FPk9j-L.IT -]>\mg5PAr2iYJheHBVCr(/m/_9GURq^37j+*P',lr3VQ3G'uGke"2?6e>_H;:"64c"1OYDEuV*X=!Ms( -XOX_G3mZI""RmSf4rYE6$6?G]i[4Y\R0c3PU/(Kr_^: -^Db4D.t0@=G$21Ij'J-eR;Gdffk%d\\cf4UefJXRZc6[nXi1la?pFQ&.TG9k1V3Vb,>iA#/)XPC=ZRa[ne'V%+F0i;;3cf9k!+_p]Q6P.nAE5-?.ab9KBCJ`5J\NMH,d. -T<^:in=rkR/C1RT[PT#]$'+3[qsE'lr-- -Mj;p\>S:[CeP4n(KgMK]G.Ii>]-#p=HSgjXqm/ED];Cu'H --\8'3k3q7ile*VRanj9kHY2k2%o07,[L`5/C)sE^ZILAp8[n.Xajjo+cXn0G7/S0=Dc)O4"WAu"_ou+_ -`r/:q9l8-VgUn!NJOR2YX!`I%Z.1`@IQbolB"Tt"MTrOM#=>" -gTr\Am`s_DIS+uK`C\JCgAJL*S7M8tX"4PFHa1(:iK(d^$2a=$Ys88dT&BV4IJ`sV>[>KP)"dM>=S9s^ -_OC0KhVBp3B87&E!OH$;?1o6,<"9h0AXD0hl*Zc)U_QLo3+/(]G*QIsl)(`Nd^`F0tO\=pV$'M](^?^seWqd3A6k++7#N4.hFZ -:P6"5qKYi:?g7h'^=Ptopj9>dd0`Rl)bRiJLjWY/-WeK]RtK`QEaPPGj]q5f*-udS['XgCS9.*I"DnuF -,jkH-gQ*qtknPlI$A"OQa\dlJ,I(2j`ZqC;FM?PZZdm3KCn3I[dE8&0(9M7@k&'UsQGKs#B$lY;[M25T -2SF%pn;Kt$5IPCOU1]0;rXV*Tjnek5(lfKdflJ6AnFnji46c8>hd?A;YLrD\J)aZXo/NfAIPWWQGKeN< -?.mnQ2Tn`'\OGA#ht7UF:HPTtU>LAr2L7'Ij4([(JDiSuZ,ZC=fQs-7"h\NT4E3J*;-72qm]XsOi0'@t -'UZU&bS#.t.=Ql^[Nh>dVfl:"kZjs/p@-74LYn?PqHZJf<`IIhP!%?erTDnuln-W,FB;+tU&I.Vj^Y\4 -kB^3fWN<@.95A7BALLSPps+m?3./b)]CGeLNn)iYGIC]PKsEG94ar7THh%"A^8kht0lrp5bb$V!NG!bd_i_[F -qe`I,IHn5p/9>=TGV%$-G@[Sl@YatC=e<\')n"kA;M&C\[rpmqAc(S.$C@$A7c]4Z^L,adkT4F7ZV.pP -WlGuXW_8KW]%E##9IhjiY5>[!ZaCaM=[[_$Z/8JDD85s2j\AZQBC4eYOH+$-IAmjrD,'+ik*']Cb[:5t -AYWeqADYqf`ET9.IH\-:jd\=ktbppNc@CLE[87:fI#,*W*IfY<93ta2,HNjt_F;9p5V7b;C0/a-WGc&:T)_H7!qXD1mSQ%4Aj+WB%a0)HZ\K!<)13#RsC8g@0 -fkN/']eSOi`A0TCI]F)c%W;3Ib%m?PbBCRgRqjMqHhr0IafU,ob%j@-ZCA4VqO@BmCW"E_dIm0iVt`&3 -?_kRU(,9>7[,gH,mfJR$m@9:dWjYqso_G!Etf(`uc+U^<9QKhUEp>2Drmo=3ZZSU&86ojmtR6 -(fd(AZkI2q78[[)+I94`GuAK32f"qiqoVkG<:P+3Ed`9"E,LtaY&9\seImckY,,1F:>/LN]LLIum[cIE -K71SPP8oK.Ti[@F%b.5=O.D!&i(6qO.-!@rqqTj!]%9YbCNJp'RJ1uJSn.W$KB7Ws/)e!?YFAZa.Y-"3 -8bYA`cKj-1NAUj62>MVbfXBirb]X9X!qJF1=m4<*0Fa&*@]CI3XgirLq=m)$:!+NKnrN8UF(+G7-B;\R -hhkK$d`a-?4^+niI8L\D%\<&Ojolo&e/7-s>i5)GRE[:Bn`b20\>D<`hkhhN&H!&4bKfM*u9FQjfbf-1ORq-EVhe6 -5E?=oc*?bc\HbP-Z?m3)#H)8$3ai%G9p_EgFkmr]jg/kI<[A*pa)pT`'3+Y@t!\NSP_kWATN`9p?fN5:0s+Hm=J^1 -4rWUN(PuK;%fY+iq\!4Sn4UeGS:5P*e7\AY(%8KNS#"W,K-U*OF(=7B?EhFRfY,9nqAJner6k(E-&*mm -Pcd*pMXGV@C1Sc8@X!O:"?c4=&n:%30O0d;J^WCpB;En7do!4QBOaa'E2Fc:]?i<*2m:6;9mGn"F,ESuemgO,RL<<. -enS6[U0(ac94rs`o$Xd5BF_,e'_dr7h#fV6_Q$'>/gobtr5q%l5.TE?[PbF6k -m`7,$KI4B(erpV$kN7`DZd[97m7q)uep"413;7a46!uS+*Q(.>cQ=`Mh4@db[Jb$8nJ=ucAN-tLMsF^J -dJaaFakH95Y8-i%1WV"aYM"RXh$[@>+)#" -2g$af4OG1og\Z!oG#$4flF+i)N4aS9-093*s8L,%,q(d5rfUVXi:HfQrJS4jP%`ZV1[] -eum0Y'GNgBiA2e!*b/Mq4I-g/;=3NA`7l"/'ud3cGP7Y+S*1N'8U+_"*hIKG?ra,NoB\ -c+I&U?E%^pD6:B_q_rq;ZR;!OGTJ`pKg.388j6^$@#f63+1sBQ.6\=\2=eGGl9bF>q&Om/C%d9*D&56($"Z*C`9[;oCHBn\mZP,u> -4Bhtlq\B1%MkbJ<]4'-BoQSMT+^I1,6h/AiB.\5nOu*)Tqb;W*MM!a-hQEkld9=3uBhZ3sdr\#FEUYL-\OA`IcO3!-ea/DW&Qbf&)-BPo60 -6Z]LQ^%4<;hc>mtp&u'k&]73t=1(3o_`-GcceOSPZU)3B_l^[6qD94l:P\S:Q!A,YhF?Ph:fZ&P`oC`2 -=%/jHn*[ekG']5#.ZE;aY2aY3!CN_/"_K24IRRKXTq.`U/8(52Y]Vm2PjV]b"p\oR%G;`pgRb(raHZ]Ii=GiejlG@Sk*E2d1rajKtr -oV6QLi-'8s#AERfNGc@1MXim*4EQVgFDb@OaZDa;ipR>mB1jgZ(ks]fJYPhKh?R3rgi_I[E$J?)oOHkH -O1!c()Mj;28'+#1BCrYRA\ujl&*+>QBLUa(//&Qu\fF(8]5\5p+@b2hf\?JYZ=/*gs1e<0K& -&q;lqViI)[Pr;b=^@P7NduEVuX4cVPNhJ=qk.cnT^FPR(dB#ckCp:#lU]1tL@'9+P/&1lpC2lG -09.N5@_@8Sc-RH:Q7/%Y'gtA2cj&^dXN\G7atc6_$!0]:?L%b!N%7pe5DYPbXj]D@U=p/p-X=lH3Ei`n -_Sq.NY8K&edD99Zpl+Y(5RiXrb;X^AMdiO.T6W?smR0G8@-n\VS98(Hm#:pY<'PV/$tfZ3j`8Smk?T+7 -4t(?J6,T:And)D`KS'dn-QKfcJA`^U1n -U@i?@ookuL8ods\&&_+Lc?DI2#$'iHfRk/qi.+!)khX9s=F$HU=nbOE2B9VOWfrRSV@bG*D@e_M4)];; -(s,-(grS4TF(g]*Ll6F_`rf[$XmX))4)^UC)p(s[9AULpUt`[RG=FABr\BnPESdoS_Q@W+aB[*Mb_q&Q -g][qAbk'gf)M1JMgY5qPBZjV31JbjD.dth;>"unjA"5T,0fDA\M`4Ae9ocX$!@)Nl-=WTN]<)nZDYVs$ -LVu`Z.qAbA\s$UaoHM*pL")/O!$E]/SG%0,8A'_&\!k0:3;hnACtc -4O1g5EYQjW[Eb_=-#`=!ZgP^[>j%fMVUd$P?5-,<:[$$!&]9sqr0JLTrVPF[jJMTt]buPHhMc]r]bR]: -Qo?7=@I;V8_j_8,0Q0BBA)0m"jo6]A"W[e'BB-?Lhd-?L^: -WpGlYGeaZ>)fL6L^4]Gh-+DXO)kc+0KW;>WLCI@GMP+HY4hY+YDnI_E -=X3!G6HH&;`tjC_D=UD6S"Q?*N2KC*!V1=`4tcI+5:H#TI5hW;GtKbH2f"s&;D]Wd\FWbT0)-_t7@P$a -;8ZJ?aIA3*pi(Itk -qVtk:#;P71Z)G#U%"AC4]V1;Eh]*l]6pHg-GH\=dXUH\8?Tu6"0=.n3OLY+.bdu5]5i4\Eq24S8%uZ<- ->:\Q<3.>9J7LuT49nhuC;Oi5I`BG=A$ -D$\gBj_)ML+j4UNDk0qJeg3KSG;.f_N -^$P65&$NEo)`+1^,p,:K[J)q*-@/(Jk^GJ8<$=usVJ&7l`*!mf@ju^8k>sW&8Yei"j%s;.PkmH]Y5/U4 -\/"Z"UIk3Z*d%URLeY@OcL0oPa)4uk6F_I15oYQ6d5BpEc?B5FnV3kG*Bg$>@r#IO*07V.7-a-]QkY"^ -O)`+qn>5!VkDr,1XBOtN!P?BqG?%?l$&3fGR&KgPM8e3ZH0"`D)2Od=WbU6&EPu+G.WeL=AaYnpql(m5 -4+V\J":c9XQ#2gLOTZo(>Oq_loE$CP=FPFSbmoh4P%(@i1CJECB -:/lt&FNM[ogo.s;WCp$'h(pJ8YRXqCNfpah_)g=]iPIRFlF`t\W)pW4d,%DEKqhK#hRa#WUVd7Hna[S[ -+llF#5"cmU60M;9h_:X7m7t\]mX\N,ML#FP'/`-gV'gTNOJpOnY'O37a#'XSZ"O6+/1YVc6AAQV9ZMtI -S^a;TB49?U-Hs_j,KPpSpDTBj[R-o5"HTi3\*//b/UMq5`S4jE8lArZ5PYmKG17PP0fl]5uZ@I_u'i!gj5 -Ag@h%Z_K!ocD9koj:.iOds\us38K/?$"4NsL,`YhL:7rmHf-f,?hqUk -QCYa.?Br_bKd`'\Hb.2@UtE08T>VIlicIWtZ;SAYn-B9S$pM_jrg;-7=o,_/F>M(uYZ1$/3%?OQaXgV) -A4PThrp4LmjuJ@6ZGlXJqTsKXFFH"/5X)guapK]]9a/hh43!"`)ud?,9kDL)H?h>.\9AKaD"SO!;2!%HE4I,'.=tYVf_%P8FC7>tG>m^MlVSIAk:_r]$T#Nf^#V(&Y=7=e8=io2 --J]Tt*SFQj/2/KPo3B;>\/MM4DN[GJLd#UJo35c#Vai8.=XX?cJAs-DkFuVJg]CLFL*+'Db;4p[YtV"J --jY6'&,O0,tJCX6o'F'9RF40Dc%.c6FE@(qg]If5'P+tCR5F(*l@,\8q`S2H;BBB%h<^^G+o>[E.8eoC^">>Ah)%^`P_`=PQ=C%!PDA[mL_WVK4XEE(P?IjKM"R:=T+W$!;^0e[S<$UB -ou;'G#H"grVaS0k3r:p66R#sNVaRJB1]jFi;K%"VjD4EJ,sou$\>M`r;HWB:O^U?;@/tMA'&dRqRQ>Ua -KMl_FG18"DaA7Y!^@V?u4g'uK6Z;I%42Meh/n0pm[A\Tqk -;ONSD^aGp:@RC=BW)FaDDeCq6PW(!GMpE!]Lg_[4T7fNd"q?h1R"h.6#%?imtOF -/qn:]E.g$B*2>lCmTKr`e6Qj*.TfpJ&_=-tW!?D&bU4n>/9,Mm.HhE-\<"$Vg_dIWSX%%KM0J -kXFZ#4QI+Q-jZ.rXK+]A1)_j:Q*/J]kA2:]b3bqX;2X1+D5@8X.!TssF2,Fnk$6H^9#G\QYk$ME71M(: -Y:M=jlZL(%124F%\#ZO]4O6@$gZ>s\RjfhnD\[B)8*:Q5^C]$1`E8.R1Xk>6CKlKB.eoH4jn=hVXf0]=eW/O?h@AG,!';Ld&g5%[ui050CX9tr%0DQK&bY[c$95GdJ-884qN/F\]Ep2X&=]nbT_G2*_)CY;fa?L9C@l -hlp0.FKB6`m0;k#'#LjNIXc*M_&iGt"bY&6og=(RqUjp#YDDgZUq>uq,NZ2@%`kuo-JpCo778SPcu<^#$d1cO@+ -?#toEq:r.ac&I'_?d4SB[(>CSk@qOZ4\HKX>I:CKB2V@51cHPJAkR3Gb$OSL%s1[k".ARk=;]V\8RTm/aVOXFin"luaD3t.A-u<57%=!!H -gdmpV2)dmr\Tt542u[j-k/R.uX1>K2a`TJ6og#)dl*ssJ(rsO:W2A*T7J\Q!5-EJGgMJM\?(bGMSO7HC -uq,3 -je?X=mDWqSk#N4M@@Gq<2T]0t9KLYXJ4ak,c-Z@TYI3(J[9E^(@t/00SEKOkgpV!%XuX2TWfg+YF1^hf -gb'Z5NUIm%DYCcjg`AH35OoIlqeodei:beX8,,A4,fa%:^PECK^UWOZO&W5jPcfMRf46NkQF3NA[;,Ic -p5,PT2Zm?%[H"Sb):bcpP#$5TGJNE$4Z'6#TK1c,7kb"XX$4G2Xi7+Q[63:Y`38B-_lStC/[T9d)4.@M -[nRe,ijUG=)2)3?.>4]&](BeTG=eh*h_^*_cXu^i[;?D_j<4?#]>7D8 -.fWG9;N8:39F`u0aI@?^f8U<<]bT(cQpDDN;d3$90cX0c57udu4f3tCeR+=2cKcX]17$lBof'C9[(Nh3 -i]D&n=F4X-khGXd#q-:QrSb-&5Fe].sph7R7OTJPDWH`9&#fA$\33_3-G2VYT'%X;4KXl.qtQh -9LLk0*0r)Y]_*:uekG33U.72QG-p?/,0h*]DCeMPV^FsPd7gPgRir(-:XN_,I$5B21TK+<8-uLfet4=pg"0qkh)/mIj8&9lVaX(TOJ`4V652WF\=S*?IqH5?)T=U7%`jYLD\X?So`e"#&Ke -b*(9ta_eMQp7F7&\k@-ZR[NugWi@s\Nib=i%Au$#\C/SO=d27-5J3Sm@D!PC0Ni7R\%R[oF10I@dVir% -414GjXhIGohW[^[_ -JNJZH@U>Q=0s0.("l=\Qk&fIE(&?'g@>5Y7fu1E8l+O-=0#`!Pc"C+BF=/`GNOr=$NB7ihS\@ifET,&J -p=:OmQcU@.l`G.+.fO+<8$]?:lb)rDG_X5PcPl4tdh6G,'L%428&+CnRggp@`&/+dK33I58F]TI!Y+d0 -GDcsuH[:QP\[E(r+Y_1r]]uW)@u%'XDdFgoC;JD+<1D45epK$`PEm`%b.H``ea(^_p%FrjH-7$9,FT=C -.\s>8H/i0Tm>h\"):CX4FB]V-:6)'.Q#8n3:isSWrbQPg#_%Q6E6YG[QC(p)WtaY9*5J+7d7Qb6,B$oms`Y2B4l:\)!O$YWAa, -*uNI6>Wr.ijmms]eNW$rn6.FdGe7b4LHm1.!p`6+Bn&c>!2YHu?mXFdo"cO9D6uaN3?IaB2,_J[&'Pi7 -a9Tm80[*dSO:V+aI`MapS?4Cc@e;]hE&@Oe>/MiEj1jr0.rKgWLEZu)aTlDfKR@1I`OPJiX4:H=0NTC/ -"L-%S+Wl*u=$Ej_BR.sZiLkm0?F5j1dr*76$O1K1X'7`+'n&cp_HXk7csBiFH9C_GrtAaVkDQb;=2/q< -okK7hrpfsY4T=gLcufqMjBJg;#i'0YjjP]_qtc3;0oYcis7gN1a++'XrrY%mr_E~> -endstream -endobj -7 0 obj - 28739 -endobj -3 0 obj - << - /Parent null - /Type /Pages - /MediaBox [0.0000 0.0000 431.00 434.00] - /Resources 8 0 R - /Kids [5 0 R] - /Count 1 - >> -endobj -9 0 obj - [/PDF /Text /ImageC] -endobj -10 0 obj - << - /S /Transparency - /CS /DeviceRGB - /I true - /K false - >> -endobj -11 0 obj - << - /Alpha1 - << - /ca 1.0000 - /CA 1.0000 - /BM /Normal - /AIS false - >> - >> -endobj -8 0 obj - << - /ProcSet 9 0 R - /ExtGState 11 0 R - >> -endobj -xref -0 12 -0000000000 65535 f -0000000015 00000 n -0000000315 00000 n -0000029482 00000 n -0000000445 00000 n -0000000521 00000 n -0000000609 00000 n -0000029458 00000 n -0000029936 00000 n -0000029652 00000 n -0000029691 00000 n -0000029793 00000 n -trailer -<< - /Size 12 - /Root 2 0 R - /Info 1 0 R ->> -startxref -30009 -%%EOF diff -Nru ausweisapp2-2.3.1/docs/installation/CommunicationModel_en.graphml ausweisapp2-2.4.0/docs/installation/CommunicationModel_en.graphml --- ausweisapp2-2.3.1/docs/installation/CommunicationModel_en.graphml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation/CommunicationModel_en.graphml 1970-01-01 00:00:00.000000000 +0000 @@ -1,228 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - Internet - - - - - - - - - - - - - - - - - - - Local network - - - - - - - - - - - Local machine - - - - - - - - - - - AusweisApp - - - - - - - - - - - eID-Server - - - - - - - - - - - Service-Provider - - - - - - - - - - - Browser - - - - - - - - - - - Update-Server - - - - - - - - - - - Mobile device - - - - - - - - - - - Third-Party App - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eID1 - - - - - - - - - - - eID3 - - - - - - - - - - - - - SaC1, -SaC2 - - - - - - - - - - - Update - - - - - - - - - - - eID2 - - - - - - - - - - - eID-SDK - - - - - - - - - diff -Nru ausweisapp2-2.3.1/docs/installation/CommunicationModel_en.pdf ausweisapp2-2.4.0/docs/installation/CommunicationModel_en.pdf --- ausweisapp2-2.3.1/docs/installation/CommunicationModel_en.pdf 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation/CommunicationModel_en.pdf 1970-01-01 00:00:00.000000000 +0000 @@ -1,466 +0,0 @@ -%PDF-1.4 -%âãÏÓ -1 0 obj - << - /Title () - /Author () - /Subject () - /Keywords () - /Creator (yExport 1.5) - /Producer (org.freehep.graphicsio.pdf.YPDFGraphics2D 1.5) - /CreationDate (D:20230821114745+02'00') - /ModDate (D:20230821114745+02'00') - /Trapped /False - >> -endobj -2 0 obj - << - /Type /Catalog - /Pages 3 0 R - /ViewerPreferences 4 0 R - /OpenAction [5 0 R /Fit] - >> -endobj -4 0 obj - << - /FitWindow true - /CenterWindow false - >> -endobj -5 0 obj - << - /Parent 3 0 R - /Type /Page - /Contents 6 0 R - >> -endobj -6 0 obj - << - /Length 7 0 R - /Filter [/ASCII85Decode /FlateDecode] - >> -stream -Gb"/LbDpC!P2-%c7K,Q-pjOd%&g0GtegtDb">Ec4b^isoIm>hHGENP`],HC'*b\EakZa?8E=O'cJ'm'i -+(>-:,Jbh^Y9,(N=i[.pm3eMj?=36uj-LkfqVsfkHiD3.7V_.E\p@-fbIa8Wh7NWq+]`,5Ar6rEa!TuWg,HZ1X ---5%Bg]#*gruitWRX&?=`B-7JG[#dW8eg0/PXfusJGnH9p#tcpCS5CU%FkDS?GH)q5Q%(G5$EoYID#Q3 -aoC&O0iT5c7`W9h<+c[$gXNtKr8m\hf.*/Z,$J4mV"?o2H%p1$_>7 -S1`,HCB(Ej4+-u#71\m'O -UZC2G*fo)l=RENWn1c">`OJa3]miHB\i>$8=Lcq.[EaW[eC0kU2)Qg7e5D0K2Ei&:4+$=-*O4P'Ac6+P -n]r2=Sd!uG>&S:1I)GPQ6G)_h['I]%?HouVSW1<42(b'Ul72\$f6iNIS;S(8U4IcH\YaAL\]EUSo>g,m -D9XeA&V)A1B_/\/0N)'")ST6]dHYN]J*COGPunl%DE))7q0X1`W(sL*.X9^bWNSOa`sd>RL'k;bCSNaY -r[dQSs&[E6IcN2tiP"7Er&\e`$-KIJ88C+bmsF=)TBFtoH233@p[nAF"p*e4iAI(4L/Ab<:7s>mf\.R& -SqWb\GF@E)D2V$fXZ%mi->-nCWb7/IQ*a'+SGtJpSk_<4fmd.ok?>!pVLDB'->U&t>%r-*G#jVF.6`'] -rqo)3ear)@*bk9Fm_u1AjY'RK0h"oMA1nbPX^3V8arG?<^_Ud<%&6A2cm$jTodoU,YuCs3F20oI>_kkd -WWt6Gp$>qbTW_?EfV5E:+_mpp!Am\q[/(`H2g275dN?$%NNuB=go$;3G0N\$=Nsaq6^X^%SX4B?N`LWu -[D9Jkf3Ud`\+J&@c5/rXX`r&1[,f4U&;-8=J*q!Gr; -ekN:!3IU;$?@Y:g$\<;p-#tuf#c;8D.V4^euEk&!a6_q.MFoLQA0SlXD` -)o6f'=^3*[k?87i2@`O+Ms_+jbj1b#3`!qd_fC"U_KdEiBa\?DN2J[VSn?W")Lbi#o&MHo=Y -)XF4h&+W+8"]DB[.URZp>*=V*/FRbofMs>12u8e -7rp+P7Ya\G$ebN6k#.*&C,TF1%A:8oZ;;;Q#BP+MRZ$.,.?r'Tqi3V5Yijp6em)8%b$(jJ\JRNV0CDs% -2gi?;kMPg\>T?CX=_QmY]#gBs>>8fa$hr#Of!,W$dp2BJaY.?\Nd^*M\#)g$79ldUX^/TBc'*lYJdF`$ -@[ReB_^bNKn$-AOa*/ar"H9LkHRaa5>ZsYQJ,&Y#XCYW,M`NG>nW(p+I]($^X[b)Ce79H:6A8g^&``q/J0?:pruTDGpkg0@t;4ZWH -Q]\a(PY4s=+Q-t)Khc$CD[6F44PHJr2_@_R,oD>m4Dp_]PG_]6*1Ld\[XjCZ"m9U)P -jh4I(1el3CD"]1#$k[+a0D*hlYu"0`!sC-]=3%RgD>gc4*kh3tbUIYsd# -R0X\]fE!CQcu'7DAT4$Y%Qo`LnOjd7rn\EodB9Z"d`cA]X4qV_Xo>cU01-&YGIK3S.SV.4M.!u?^*f^/ -6PC;Oh1X.qdh20gqO.j1dK@7T:pTV^^(F)Y]+kNQTudI2(0cp$D<]UI5IlX7dclga?pM*(F>$!o[1$QG -dA,LS"(QE;[LsZ.-dTSZWb9V&h*n -?RiVRehmV/88HZ+48/u-&9?$;\n^Uo0J#MLWRcnif7'4nFoA^R3s6%t]jh]1CfHAZ,&\=rC%Asu#73Hs -c]5QgcU+jUL\SM*)_7PW%sD*U<1t<[>.JTjLfS.3_EWeMgF:gqf"o`7/^np;fS7s;ah"T+)&2At=Z-;auX5R)XKXsW?r)ATLfCD;>a:dT,B"$P(WA:/WOXVL^iM4:<%fORR5pXd+qF)a<\`-/hbZm\.UFH?M>U7W>MsHO'h"fMl3%SN>mS -JlA=Lm\(g_@IBh`+e*i"cuop;4pKci*b5&$[sQ3#\VJP4ZJ@le_pGN$mNEG84749:gMm`1hsMZSg=nan#BS\gTe%LT`]8+DcFCi(>-?TW=%ga9N -&ENXHm\&)F$5`DZ&&!Ds?`qb*0DA:;dcuL$o_f6n:lS?hgc\U_I*Fg]&7%kJ%ln\)OQ#SeWPMiJYBY*qHi>%( -pX&1QN2Z+b145Yu(QI>Aq-GgHodene+ieU6a=JuZkHX -6iAIoon^5t/3WXWQM(qMDJMA0\4,.%?$tD$D0F8UMma@X:U=^T\JB)8/6`$n\o#R`Q=G42X2F=&oI+RG -(&`">8>S6.jS)\GQ*Pm"f@nOAhMst/ia;)(XP,4/6ecd+/;oh/b\ki!^$Sq/phH78d@s-Z],4o0YA"9/T>g:+ -K?0oM]]-&9/3N,]Vq07R3L74-*J[)DXRF#6!2m5f13cL%:oAJHa)m(10ccj_CS&RgI.MNCG8t#XKDkJt -6CHuHR4C]BdFfFV?0MLS^.R/VUL.Jm?m[]rSKqPVLfL,6Pla/:#rk_Is`Q*-+NV>r,' -7rr4dq6F4uY=/(oD9;18[si^KccuT70k"25Ku,t?p[tAX[EsIfjmlpI:_]Yb8f=Fh/Q_.XSh=+& -oT.Pab!D1@N[7cb?k2#S$GJ:UOXkNU=-kEFc+#&iNI9]N.lGcR[,oHM-ZkQDr7NAd%2RE*Eb0Yj -mRgruneL>a*oJY.t4d\4)T3E"$gN;m*oASs.>4 -6X-]1hK$T?j9hF"GtX<83eGNi?l^n&.hAU)=6Lt`8$9WNXU&R](&V*3!kD3mGeH,&=!.9-?PqZ\)QM3X -,uWk5h*+9L8`l&oMIVu'>gIZUb/-hr&72Ea967YL&D^G@=0Q/9\m-\g-A'F+K-XM<9<*1RL"T -7RcZrj9kUIj>A1fHDL(uHTq52^tbj*jiZoA3?.p?[=5qT?VV;Y>s'sn4dS-SR:e/2\sK0,Cj;X1VHY%# -gu:#(('f9&o5fI[TGM&%^\M@+(* -d1c+l\#qnLL7T1$68>lXEF@AO[Q#&>.@+*VL:5([=;FDlbVG(Ymk'mF;&`*R/.V;c%(@\4?dgW@EE'GV -[lq#nPBI,gZf]*q0cSGEO[jKNgi8kp7H::K6>>71mRBj^@,M''h -`T7jVA$:'V*e0.OF<#SQRpcQcXjWB:"*?N`H$I@3F@5p*h=*E-.U/senNK2sC^9/hZs7m,^/1^c?jGm' -2`].!bSM`k:>ul]6J255!52r](N3S8U\EaJh<&PrTGtTUU;ic$o&]WdFY-aO"*[0L,Ln+ -5">WRIXjAiWPPf96Ur,FF_^-uAaN:r%TM_4C;>Bmg&Vj-51aeQ!M^0F[q\!N^8L,\jEh5@CH9TsnSeg< -8B\DpF-)N_Qe4JC97i2iLi)+o[l30^)BMY)K9XbZ3(4(EWub$`#M6o2.3*UmKjb@e^b;+Da`*,k.Q(*3 -V\nnLAIk[UJ^Vt'rt3B?[U$+,j4Y&JTl;h!Vc`!)U]_('=L_9_AiKs+M+YkJT3-N3GP2LB --qbK;'M()G!>>_ZfC?K>ObMc3nl@h!=#e1OG,0mlN5,PC*DWkg4n?mUq/gF=&K&9]Rq%?":>FeB"3Sc# -?%("Q[#]l8S]a*"riZ55k;$]V!67]Gr!jT'@2CGi/M^FhM*B -)NhH^=ZdqW,h]kpI(2cr.;/LL83q?q+3raO8#1toq/RI2e;38PM;JNj[(8>cjA^]U+2.#cj2 -'CO*a_k]NCUBbRu^%B)oA-RO7ZsC-#`KTIb5k+&7jLmX,T6U;_SCZi)E5kWu1(bsBHXUSRn,];,1%h9E -VLodNL1^t5K?la?4T-XPaeUj(oa[qlh*HJ7qJPjf(O`imQ1NZdK(35'Pj[QNm&[Z?3LC'SnTe?VRXE\j -oe?Uis83#gPt7VF?e3sJXbJ%b+\i&pUjB`f-*7:Bg5V6D`ja^OX/G37D^ms&r6BEVjEGCR>DL5NE%XDm -j6Y.]\T"isWbdAPMOD]oES:#diS=&PXXQ'Ul_A,@FQLjcdE;=W[/M#!`t5!JK'2;>KMD9pKqi5a(*cq0 -+rZpC''q^%-u!X,\@Z.RX&%!&k%1@;g@UYd*$?C''"Ofc[VMItG4rrF>n]0a+rid\Nk>-Ip"elRk+\Hu -+tCd/RgV(qO5X6KcC1Z,3G,uO_GtD7b\&Jo2t[Ir'=B5F,0j0^[a[Yf[< -22Ms8IDr^Hdo-k!aI3EN-,nd=JR2K6T,_1&_XNu"4Z0WW*3[?@.Pts1?lY0]m9.&Lj/W'lT5Tc+me/ND -q>*3KLO9U[*7WOY1X-B0;hOb@>hjXb-tSA<3V][\!$c%CEYL.Y1QYGC+e8@!M,eW0'nm%F$;WZS'o:$j -HI["_VL9F\cXLQ/)n"t1kBLSQZroM=IpuQTrhUfS=RAl#V]]4q@lk]]'ESfWBK)agNlZp2f -#L0,/#rnnOk%/pXiYjbVtt2@>\]EocfV -@)sk2WGQpgqCq4]Y;2'D94"b"PKQO(-.CQnY8k*c3]Lkdk1K5SR82`3.S]SCnnNS1LFnY8V_,U3J*;48 -)RjNeX-&Tpl1,1X--@O;*4rjm)3-;PnE-u%'';Dbnd9HcG,nSTQWFDL+-5B[=cIjQ/$*g&j:tk0B! -r[E1^IEO,T$H\XIb/R4&YB"eH!/YfZjHX5s/,V/=H'N4O]=d\u87#_2%Y&r\&#q*9V9PJmOAIu*CQ^Qu -brM'B"0$sU1NQnJl7@C#r?&e1ji3)`]d+([U6HU@8tCS1 -@PX/]#K4;+ROlHGS77VIEAT6-AXG"T7'=RMGJ`#T`"DP+UcAL50Fk+g/N.JX5`*NHir>8lRm%Id%mr,Y -3[umMCdh3K+^PctV(dqJ`CN].7j#mM76gOZBHB%;p-^]s>81liBn5^/>:[a\e'$LWp9qAP;>3tcg-HIC -a[TffY-YNL4_E9$Os>sbpX<;7?28&"1aQ,g#gVRj2@OM-W+8iaZ.$_0[;O]sH#(oF7<)kC.0\o-guc5V -8dc5JeE2u>g;!_TgU'i&-5LGXEnYI`g;#B5VN(86R4m9mAu1;A&S2YZ=cTFNgr!IM`*90Fg!tuZtVfNb3?8[d:[nGdADoG=).IKK[2;43RY`rZR?5=KE_th ->s;XV"P%oeS2;sp1([V43s;sRE.BPj#c%f<$q`!k,^Dkg2Ap"8-prONfd89N79X^+/jM.>:Mf6gWPrH)k'EV/BjUg9&=Vqk[H'rl0YR2s\>C`+9[Sul/AkUV+fOU@QWfJm;jq+J&] -LYaQWW:qKL5o@u[d$?M,+V<%A76EE!*I8C\:LSDt&JC7p*U;j6`_K4]5\Q"]etiIu*K9iAR#O/6PM\*e -k8RTHhUIq*]!1F%$^Z&/kf!knFH&7/m\i$=q5d1oVES:K:Ro'lks@ -Z$DdBIg-gc,SUr9ZsKBl=i7N79u6&0^7sGafU+"XRdK__3D0Bm=6tDXN\jeLa]mlU%EhPf\Pa20HV;im%Iqmr2&d?iXA86H]2XjS -[3P/=8rhQ!.\S#cqN0UXO9dGPMD@6Z;?_?_\FldRYbNg-MXE" -BLVAIhC6^FD,2c@P;]iq'%u0HrKMPM8gdGN&$cM;RIoG#$dq^p'Gm'Id`pZ"c,>`[-3m0W=a_de'fWHn -$N"M7:!c&WFDK9]#D>6YlBSQ[I94KIPLeQ^.a=aop\7?:E@h%ij$1+FRr7+V1MK3("kMC5->0IJq2XYc -?QS^?K?[jV]kGXYfIY+lX7&?=dQg>qr@f1qo]E'R;t6k^P$tg0>N8C6bu7p8fkFbCeV7ngbn?E-IWP); -RVk$kk*4`UjY\8(6aEHi,`GB'Jb%GWk(8,Ij6NOZ5?CZM%@L%A"XpT&2<#lSmP0A,$oE -p+B+-cgDcrHK0?r1k"1`X)np?h,4*o;1!L$SS`02NhVffhK@n+fBm5U*k6TeRUW`)6 -efh#q^$BHVp?nm2?Ie>[I-k4[g4?#W:M=r]G4o7m9V)n(pA+RHr>g?47]-DeWahq``%I:H@,JS;TCH<% -bcGf!GKuq$qqbVgf"8?_]lt82iS(o(IIH1[YkKd,I.ltZ=sr4>kX@M68[b>dY4piJY2d3Zrc"LM?^C?- -AKJmDs'^6iOJ::jnKbd-o7MOEgZ+@^hQM5hU?EZWM4N1Xa29Z#:n@tc$fg>FW4_=R#@s\?(W.XE9'n)X -M?E%$M[,ns5,3+cd"@RsionMQ.YaOs8bNruAce*40$$nX1pX+pK?"AFo,*DB_*'^!d_lP!QHnVGM&Pa@ -,\\893j8;/k+r`AQpE`p#_;n/f""fBA45.?'6aOX)_7[!a%2XH`_@6r.tONN)o;q&aV\@L(GgUV*n0d/ -6]54QnQa,>"LYDX0UCG,Jf>r/PnT%^L(S*PYt"TWf631Y/Te.087TWCXAro-#^aAqGjW?Q16toY9,MZg -JR_F(]qG2@`-I5R/K&kSUj[/Po7YJ7Dl&M.otMVB"LNkip9'!I3o#;`XA?8Kg_kB8iLlL$J`(dmWb5J, -5-fuH,UZ(l[u*U+bY6!q%&qtOmat\:V;*$1I!&1ej-)8IB`rW3fne7 -B/)PKLng!9;.Lt-4Y+aN;^pDF.AYDe:bE!UYV:B9?4dfM3T&GK]_-,h!PCYek,];>:_[:3qfiNmV:oI<'VeK/kB%0]d-dBbB^OqMiROFXB6ked)"&C6/,.fe.S -[@GkiM(qo4UHnj9>*,F*`tm]LV'@$a&[:.*"6\7-@aOCJ;*UF*h:^L4A"d=p$.2V9mFcA[oCOi@55"i2 -Q-D"n"6r664q7=5aW_UZe418.;QJ*s&ee8nX\epZg?PAhgB[*=*,P<5`h/7rZ4&SL#eKIC;gU2KKLi/c -89&FArORK(In*ho@'ia^m[#.&9CI>E*4&XOn\DgWPl<_KKb=iTU9e.,%(lkUHso&k^NVfB"Zs"5T.gm0 -MT5$Ae:.loPd!C^J'0fa>c#7q1FinP.eJj98+N(Ska&V#I$j_GQk/k>,.C!)At?SI>@YlJHRK]\B;@Xh -:s*FDMQ^jOjhYHT"Rm*InoiM"I$miGN7Sl(+sHXrKs*FSEZ"K-]qf27!^,-@9$E*l7F8r1 -H?=I]BWKL.\QbumkA9KJK)/Ua._8,T2d`Mp$(n)25h[aG"R>4R'q4W_l0-67\m$%r#V+rK1eEu*+49r2 -=S.1*%%t]=Ka)t`9kON#gR.]9nB>X4S3X#7h`E[0g%q@>L3[N?CA+/dSdaro7a6j/_,0HFW/a4_ns-7L -?F$gBY>o16$KjjG;ctoJ08d:(73rQ]/$UBsXUjO-;?BjQQ2%OuU!2:)#Uce>[*%9>&u`s^[j=j.T8t6o -24pQfOJ+7WKO"ZDN5?l;NS;E*JWMiP[KKcIeh(A]IAo^g;h&Ku_I:g]6F"kqcg*PQ$1?KW8L'-.!#I(3JBZ2THNU-"*b/b.E,:nrC6&EUjCEdISX -od4r/Oh-9PUZ?**`aE@R%F508^@UCl5$WE`C&+jtf;AH8"tR+N9o[jFWh8c[Ki,I:[\q[FkOuPpr84+P -\;9=+\8V<0*_=3D2KSR^X[4LK(ME900o'OTbms3`l6o61;6]W%_@8@g!oJ[2K9E?%M[`_JP\,7qD1[fG -:AUXhk*9t[OeWfJ,<#I6*q0t/7JOsBSmM((iM3iC;&*)&9OJ[6f.DeSN_/GC+ -d`\bjo^]#[oQ'7H4l:RCqLA,OiHriH1k'3Q)`@Qak+TIQ<(Vk8Y=k>EYU#\;[U^#* -i2H541>amK.ShXS](VgY"SsTm-r+rt=_fpV',Jf[jh]^iDjB50IlVb2FP]+CI.FHN\g"&R2aat0jXV#L -YAYNV9D9@g]i!<)#B%b]]JG37r'#PKX\'LGE!5@66bcZ>+E&8UFF$@a5H[/mqm36cfZY>KV_`41 -[j"hBe]2n9dWO]]=%=*7_ltckCN/;a\(!+tPAsS\_8Q^*nAX -0r&@?'n:_Nd;=RJ2qK_>nZCP@7=Laj#ifA$65Ko&"G\C2_(3E8WT`(MsWm>lKf'7f1mEg,VJut -nobeL+;!1?FeV@_.'OhFSlO%@8e3If_I:\Vf<`\N*)3#U8Xg^iZg^^`7b%2s6LImeG1iZd\m+D(E/e&m -E9L',82nT%cu8N9?e0tH5dk7'2RBE(Q>khJH&gp,]MNkK>.LV;*bj,2]DU-/.DduP<=XQ)LL6K-]glm0 -,Hg^Hl@^t@rpZ26/CMBB&YYY[79XK7//YSg_N8eBm2E;+5t)0s+e7+oPRah'L?RIpGU30sn>hic;D* -oPs"4-Uk+5CBYX2Dh;<6bARI;nnD*!Q*aS!X/!)n9B0]fpiX@L-AG7hjS64AUagG&CTZf%*N,/7Qi!-D -GP("rQkq,-]_PY=bhRKa>99Bj&k?A_QaN0.MLOiC:>P$E9JBF^L(uG)pJ%P@c^3T#hQ.G*F8(`a% -O0FCD+0kf`IDkWo$i[Zb)9W(F`ugU*HqlKa&,WlSIdrHu3N"\5Pj\M(ktDH#pl&n2nY4'6aJPl -geLs5@j^:g->;*^.6]i?/r.Q+RqfbQ^bJ18S\^&LWn\F\b3i5E[1,O(?@gCT/jc+he-*pKUaS+@.4m,ns -9uCcp5FM7X4+(CZ -%+KF1HF#Kh?/i,8AhSR:"%C%WkV_,\gW&2.)i%`V-MeBiYFuIi*,EjrGZf+/6bU%.iQWs:Bc?+m]X]EZ -iL\+X4o>mpX5U??P.95rMYd&+Sk1)AB2la;Hh+Yr\Ym-op`mM!F&2KRS6"cl)Y^WZu4H -jjn6bB"":hGrsRtF/$9NF,$jJ&XG;p?hVsD]>'pJ4570$,4cPpY2KQL(WMj_L/Q:"D_]^,a5DpB:X*Z,[n-,!*ZU":p$9[p``''m+*)HuQ@(4T*Imtd:W/"B_snahN#]t!l; -VPi\=6TM;Uhcr1E_0tOuWgEtH=J)@\dRMQ<:%Fk;Y]CEjDh?X[MS+ARE^UY/:m<1S)^%Chj^9tV9%]]i -e%iI"oj9[4)]AmK+XNRg]TT>)_2.<7@iB*YB%nZ=`8Tn^YO$PgGKA57Y#+7^\$!h:okRW=-IQUfk5-XY -11&(tEFu?n:nFD70pQTIN;=dA:/b2/#AG]XG3lOVIeU,WfrM\ge!((_5l9E:G3.%3Se4teZ-KFq'U*Q* -.5+>R^$W`2-hrgXh%-+;WfoEqX2ltHqih9[V^%d?GDd*4QJpTBc_8p)g/?o;'_`TH:3X-=(UR83kIOGl*4`atf^)W; -EhBW;NU)f4?d8J4"[*4iPQb`*V^tF85J)'jBjbF'0[I/8]o%(AhZ5/afZiA\Em-0 -joFjk'[Q8kH^-V2=6nMQL\]nEFmN+gr]4W,o;pGVp8VQ-3][1fb(>*7l]_SARc1X)7u?DqpNQDoU/SBM -.teH%/e"h2a%,/>8Y0a4>\/6t^_]fkq!pFJ-L949lINHPCVUpL9DE1*V8s"1`hbW+:0!2)ZDMW8(\tEn -L>Fd?p&A]1_Z(Hn-Q"K.`OE+=<8.09ikNO#o?5ljpggk68;_&s)QH9QNqj%-Z.?fnJ0W4N!h&'=%pi>HY^_4Y&0m$ejKQc1Bo6@QCBdT$#%]#N-M6]oZ#fQsYquk%@O?#]EG3UX%\@ -<^'[a#`HuX=>?j0ji?Z<3:/@d^G]9ZH?Rb((U[JJS>El>7^5bKP00Ip,[.M?RF3u`bm/C7LNLaZ$H?&% -;K0eG?`B?ZJ1)o7U8fUkp5&&8A)W@[=e&H2r-9t0+YHINa*d6[5/k&2?9MOhEa"Ige%*K'bLI5oNJElp ->?To]Hn:nCQOOoAAUMTg^JcmaXYkp(ZA&q^T$?)a/F`/mqG,GE_mi#oW1OTJK@ -;C#>GGAGA\lIjclcOQ/!e4]_KFT!r"CNF5[IGMO?g$%C4%II!S9t*m>?/2h^UL[i&.U(XZ%7(lZL!dPf -R@>8:O.&S\!L7&n?)6pG7IDHFnfL5j&UeUsQFpoEUO(-+l+YlXh=dl*4#K?_gIoN5oCUXh>'$,(@R[i7bOEY_'P3fh.lioG.kecJ&cY\J$P^%ZdELl38Vt? -p&#j!mhP#kY8Hur.u*]"VQ3DHeoo""DCO;2/]D;rn9eLXJW=dl'*,'@c2*>1-`_N,N!)cq#Am,;[t5-++WQ9oT` -o^)>.MOP-`9i1@3\1'OTM1WDu;Z:sW?id'H(*,5%%7LOqZCD$ce8gr)QR$9Cc.,'BU+SG+o1@i -Z&use%c#b!Fp,d0;@aY-RDftu['$33CK7pb8>d2:X#b0#s.c/sb[lr+Q^A#+OKoT37Uhn65(bA:ZKYH4 -Sup^T=jKj,8;btLRXNA.0Bt2(?9\u!'[]^!$Ah)DW;&JLZeuNrBj*$%AFs)]Pmnr0q3[&L*>6t];S!#UNcG4Z._S&rf^m= -k.G7qn)D!C*SpW>/j&pVXBJn[l1tG@5(cLTaUVWhE*B"^T4GuM7aR8<,Iu>1ftj^8[d7toZ=>/*(L1#5 -Tm=5RqZ%!#=H7J?@;ENOl4GtQ$ga/K9^I;9<\?K1ON$Fh@mjH]/it%G!XqQt\9&QR[,A5WEMMCELMU8p -o&Vs-S"_XL=2;(V>[!/G4TA$o`]%2DFlcAh'A*r)TAV4^ccK]j+m9SOKi-TZkm;218 -g%DH8?/19UoY*?A:W(9#.6YTB0'4Ht]oTM,otHE%mh4N(he\aZ$^\2DB6UM5h)WO,ppkV3Pcec';c905 -pKB*)]^I9scF^LQ]j"KnIdc*PD=7P8]m'4?ZbIr-b=rSE(-KFa/=WQ9LXpt0],U40oS5pkk/78d*@5BD -#79NRK`1^jm*G\F65]a<^A[=[/\InC,8XDEqq=$90GSJ*@F!)NWKQFr)mMSKO3=9'dm!!srJ'uoh9*$H -fQYQWFM30DP[);pFK\s[oh*LOtBCLZ:6\-??Ee<2-l.N#Q6IE -9hN!@`)@./9<:C"s!@!qm.WtpZh`-C*-B(kc5]4'D(qqLdOd"iSlRoApFhpFG.j6TCk%`J(2.$QLmMt[ -&5N8%Y2>VR3]R_qqS9;g/0QJ$bhtY!:S2Wr7G]%LNCJ_LF%kb`&E)V_pWB]A3+c5VU>MEWR>HCUN+gUC -$afj191+&K%,4iDkhW#G^E/:OK;b_QUaC'K&%a!V;7tej%N^;8<[efFI'j]"/FGL`CNk'jJmfRd -eiq5S"E%B.)uO"TT[=7lO5]hg%*cT7S!kb9lTQ5IYK>53=.@2K/=*gBMDC&JTCp0:ni3aCrU%1K`TG!K -&i`@Q7n&Wq'3-)X7b$]>8U&U%pGTe2B"kq&f%X@Y=9.5X%a(f!['@j$^oZEujOsn3Ql+P^[(DT(5M,HE -Ok%q[L%1h<*Y,_8>s,]4A]15!c&qo?H28Kt2BM+tpkJUW,#FKCj*`VokqVbAnf,=>OFkK+N**f&MN`-H -c5V++Rkf(6+Em:UmZ++nNc%@Vdl<-B?)sp#ar'&6+)'*X/k"4aoW7!o6S3*9dloP+)H!i:ah9\_>s[J< -bmki-,J2p2S6!46\\k4VF7l8&i%ZnDX?L61BSVk*=B7l:&f8f-MT28@>dD?jfrJdB@ug9A8:@#qZngZp -qpW80'T>7FlFZ7O0"-(Gj3N@A%esb[Ef>$kO5NPb3TFhMgMHo\rm&uf4tlkBj//8lN9QhM0RFMcl'.?U -oQTLIoo#u9'GStm-VBQn[OrgJbU"6+M9uSo]HP$pIBBr_3ab32:BdJVYg@'[oEhLW7rjVtpWUq_K;0?)C@]b,8\M4k=NIZ#+-?V=[O`EJa3=AEX%F9^HT -,mg5jX4kpt1u%7q]pE)#Vj4\EfAIQ%'1,)+6go&eP'M0:4=-IZdV^b4K6GQo8MKP\_7k -[;O5?>0Q71NlO5iI.NTe)R(K#$!N[TP@gGPXHB=W5nbc)N6=gj+OeNCGM25GK#b\e4"@N@rE4[]\$P;QA=JWR5-U -ef^AhpMq*+Mr;HX1GLb^g"oq\I\0l7Y+:e6'2a)]Y.eZ#ePtq)GD!i_R^[`-3%q/ddbWpqhmVIS5PQgB.;s\$aSBZ\dLMe^W2H -;6/p`c>ON%LE!_,D(PR%Gi?,s@B51@euAW0r78-_Ai6\j*4:eYn!gGVlN<.<4:e?=q9C(/+&,0%C`.f8 -S&\;$KKhDM2ur[8hm#EGL\f9pn@nk$hlJU^LDM,^b@ES5nb^t>U:B'"Ogd4YMe%(QqtYZ?s+9o,qRK)H -`JiXU_slY]o8J\"'20]RT"Uks5nru/9i9)+rZ'`i2Hb50fMVt-3cpt9-r6LF1"u9J%#[kX&[I*AjpYr^ -#P"Aua$(u##O[In*1tOcJm(;Y[W&UK3-s#rBF0]8XbmM5D9Y7_;c,f?YEbK; -^YUJL3-#WrH'hspG".,scCK2[Q?i>l.(r\*nf_7tKr@/g0(9rSW[YZ&HhKn)W%Zfqm!lTMl*ba]Sj -Y0l[gBp;Q\hhsO`R=7r=7G.h"\nrfa])E4Y[WR\(d@ti^hhsYOC#.15o_2MgV_eFpB24iuIEfjQ8[QDE -CTUdi[P-@\k^mj&%6^-0%rpO0JS/A61$_edF>AmhrN,h4Y>:^j_S]LIHr+cHX"^%uH;i7\<-`0SNp9Ud -Ot_Ko6dYY/DK636iuD\d5@!HNFlsJbb8(m4mL)a%gTi[@\u:G)+tk;t%-X;@@6H4;IZ`E&9+\Z_1Hk:8X]' -;_4]*HujD;&:YaKW$(LjQd@&&=:Sk8?IA_EIRs4D3'M`j/@S(\W6S/lGVMs8S/ -V7Lo/c:%WANK;oCl;?\\?6aWMWuJ%+St'm]'6"T)8D.C2KbPV`qEM4<.fHG\J4V2ekK#0:T#dsfS=e%=-9h-[t]>[o;:9?;0'-= -e>#l]2tC[k]"'iaNGtFP6F&:r%e#98_>ck->Y%/F=>?BP2F=$p0 -C^0*Amf(tJFX-^^CYGn)m*#d:3KT9(jS7`r=g>N3VD!6mleu/QnVG2uZ$Bq8/_'UC(1Of:J686L>/q2NV$_*`s!8?$6YAKE`uqBdYhn-0ejV;n+e -R/h`pd8h`"b`O6[]1'ln-At?-4#RV=c5g9Ad%#[9H3N_RbKHnF@G;Su[c;YfE5(2j_kcK@ASr2'd=$UY -iR7llYJ'9IgAJ2KRp4J+SFme_10WdB2-nLA_sjOWQJJJ/MOcY/OmXXOBLPOr@2(2eg4LB]32Ll>8@`.C.0+JZsiR1=4?]5%KD@8 -#&\NF\l;V4MQM?.P"Y[h:$Z`"njs^93-*_,Cp_UDj[nqRD;)f%[*r2@9B[;AToFR![H`7ZD@Pc?)IG]9 -l@F-!TC&QWfL>Bs6oL'VjCB*)V_:nF-YOg]@aAY#WqD0FNQ`k^^]\\dWDKeSn&2_1m'c=>4*6XF6 -JV_-h4MN]E>gMtEe_SC>)SW0`Yp+=UD4PatND3B?Z`4W=0q?CJjg&dJZ`1$@"u/O=.-[%r+BebW26NUf -(M\1.):O6:?-aG+ZcqmjX]8BE=3<,WAe;N5s["S#%jaZrl%r#EQlZ< -2NamkZQEa&cFrIF&hr>E/W=+T'h$h9"\-JlkilS99\QH;MdUuHl.U-rDHsMF=iZK5A<"@XIUi'm&1S.t -Xg]gUH+.C(`=ZB-i\XY.2?T.B49j#nVkT7N\c=77s>A*%ORRu=d]KTB(Z -',XW>(?t3eE\TuSc@Zm4ANafkNkTESgMq5)eZtBJZt<_d(aJK1SJJnf9g\rDo`m4[SoiG1H:jA -X9P>17^]Gp=,5i4gAVW&OEa'867P)\8:qg6,2&@hn1VADCg:IHg`_&K]%K/"SqN@BMdaUCo0u=KI]&O" -Zmfj+_E&f.FjlXQko'_hKAe,rH!>-jGPph5mW_l%qTCYMEAaHiUVGs0;Qu*1>[-pf#Fq$YlB`l?ItU3( -uGT:.H3&E*Um -WYBC2C)J1jlg;:6,kASS!RRpc[a`+0$R6m7I@,$DP"4@/8+c_1E0e.@;6u9_lZ0%_.[j"R92[FM33hj#K -9k]`gMgAO>[qF:1Jqoukq2%)hK"CfZ:YaVDtjqq88DkuAG?b_4HUGh;^XcEL/NF?(A -5[$T*;;V9IMX>\\g#?b(FP^F#quR[#eI*ViV`89/&1hfl\@-VNqDYXjVNZ/P*WuKHSAJ'fj9>]UH.9qH -,"(3rkLk`s@$jM6WYp*B>2BPQ%')rpN&5RncRs'>'n@7p:DV:,%;*i3`[OmR]!JK4^K#Ua+(l%K4a(G" -'WN7Vo?,b%A_+aR7iuAebh=,LJJOk'Q*Ri6f,=Ns2ViG@VN2eJg5;Z%h"7`_C>u'V3<[/!I:f7PO57%cSfU`d+[m?1EP*_B -oV6)QK5n&0*HXYSTbrV\eS/$;($6'Gkn5#q:h&@sK8+RtUk\@C'3#V$.-;\V!qFu6'olF%Q\P%.%Y"UT -8DOd1'KlPMn%_!sg-&^<7U4CkU"KDrTT_*G^Y? -aLO?@T5^("[ph1mc9H;eEGE-3&Ss^;[o87D_H",3i3it,!"`@$3*VO^a)OJCGT7jq5-Zg*D-7h]FYh4j -\SFPY<7Kf*8[`/kfNUl=ACBI2oCgb -8VKf3o@1-A"oU:OCf+a"*lD7#<9K2^S;4a:SL^1S^BkqJn/#;8,k_SQgCNBI%^+CDD'1rtZdDsrTcqb\ ->`/q2Gg@/M"N=_MUG'6MpRr-eceaWE8(G.V?kaXZb:35M6*3,_2?[[PZtDi%ZY63KBd+>K_?,Tsl_:JW -5gMFLSe6:A]L^6kNl2!;!a',H^YK&PH7XJ$kUr*rNo1&?7*j^Ho^oC-<`BUu1-`-S\/AR.&h8=IG_8b! -mJ4QP<]3E(F7RPn5jGF3k7bSd=5a,TL(EtV,C_iE7o*/-d-dQp4G)6!$oPj+M"ll@M`2A)EU?[[&67Gj -o$NX\@RTdN>F6^W^[/PV,r"*pbd4T%6q@[CX#^]D"KP.C/oKok/(L`M^V!^K.eXra)f8`W=]U^Mrp-&3 -'?to@"2h4(Sh:G\/flN3f&kKDX%g09Aj*7b9qqpKN<^c2cH]JS12rJ2i<\#f,RYDXaD]V2HFhbbhb,DP -^g"FEYX(9Q>Yl1h9@+'Krj#RliB85M[DhtOj*B53E#`jH*A:2gYFoF#hM83)5d)/&#-6Pn#`E_kVR/Gi -D;Z+>`=6"ln4Mq?6"$2kl#oD7=]i)C`FACM$HS9?G7p__>9O]pJk+LU>P,].i8n;ZWrFi\^pf$V4QamM -XI56di?8m6'@n6`OTBG/QK?%-?Y(EDuYW`mdi;AEG:e;ZhWY?dKl6_6mF>DFd$-qUq:GW --V"M.6QGW0)lMbG&)?7./!$bjX$8:`bn8,";^.uS<8"a<@,Gl%,g,;.dhsaY9(gcT,91YYp`,e#@oCQ` -l0FXtRFiFbV^cbf*/.c9/b.i^d+iS.8)E$JdT<;*hc([):S--5)BHo396FcIb**1&TdRj7@2o2.3-En#JC+^&.)G_ei(O37?!Fd8f%r=d)8m&9ldd=dF,p9,(?X2"n1'Q4E8NIOBi0[k;fSpVE#B -Asi+Vmj5G/p`H+K+F.9@'6N8=F_#"DZbW64b&\H&[mBdh];N.t%>XnBZ<rLWM6ZgW&9$r^Erla$"%NImtF\9U=jPZq(a@hM>+0au/]h@L_*%m3.b?"?d&OJGJ3@3Z"7aY"gOFf]c -T+X/)S^Oa5'&f1i>umjkS\!K7,sqEil)-ppde.)45^Cd'h5Lr8ukj4)--Gn*`0e"_D0`fc\fXP@AXA_0ftR8ql`?GeW-DB -k/#L`os>DOEDdo-F''^:Yq6#TIk0j,nQ-kYm0)(gag_7t<""iA+c1S\Te&;B_Wi=d+?A1<+0?)D(F -)#>l?/%J91("]/`T;ko?B4*:LM&^%8/Wg6"Q_M3@VnsF*ST'QW-:*q>(%#q*Z4% -OQL81e#MVN3+FMm8aj'55J"nP&t"k4\rQ&).n%XV@LomMQQajHBj=0/hotGO`qd'ifr!H3n7D5'JX-Yga"Sd*NLdM78+h-;q'nC;O)Y<(+W-r.@B>NYruCA37hpbn)s"P>u`ug%WM5hItN$]_AJ4 -?mZtbXC8%'6Wj.uQQrbA'mHm^#2-?2l(N!ke#I6CWV!:AI(*1\58;Wd-6VZHtatamgBltZ47E)NlDa&)%Rbt'deX@SO -O!F9rIa5T10fksne=*SIT'`8?_,H",2aq:A*Ts:Bh)K`E.uQ&AI80_$P^1`[;_hBl7a'pWX0#Wg`:D$g -ik__2B,Co<2CqL!\aD7%7[YdXLmaF$.KVR:lN]lGW=*ddGf(o*O.IrjQ;*8/a%gO+?Q*@W*d(!!h-Y*. -IB'C4Qa+`#e23LERBnX_N=S:fegf%]U5YreN=-sh=Sjg$H`Ljm=QR[:XQC&TEY0ge+L?6N*<>]ENC\8r -_mkQak?6?1(eZ3DP+20/9:VgeocoNj0'ekjO[SUc[RV>/$FWaFEKq/LVug#U1*s9ph2s[bPm_ddcZLXG -K!MjBRc1e_f^-L*C[$"q_;O@5L>iCrk4QgnAJ.M^mAsTo'mCBLmCfYD#31rZ0@k>VY^%,Z[&VRHA6s5t -RgQ7.Rc1%U#",603F:QUj*EV'7AstR16Zl*A-k4e(%;egc[usHuATa@eNVBHEQSdrCV7rcV"6Fc( -3SLas\dR'h=ir% -&S]Z[8!CXoO4Z,WethL$MO`+jVeMmRQ'"&T*+<1%[Mjg7pt2pVT1G.M;C8_,C!:c.Y"8^u_CXunL$1Q1 -2/0-s9Lk;l_=G;Tq&EAY:7="Q,5XJ[US$b]1T64S5W@u2FUulAWNJh]SO-$haJt;5h]&//IdP4'^L!?c -pJ*rA1jEd?i?8l.-j.XH!OkBejFj5k%C!KC8?ceUFBN7kD -)I](V1WD><^1b=((:b-S&/G_iN]f'I'cd^rL:2+(?kT;i;E"!AXZRM8e`-CpFBAXYGP)6QOM -g7%YOV'aGo(>#AXhKC_i;XSM[34ZmnRP_"<*)2kW -Tt)N;'I6ELcrW->B_4_fH;is#\!JG$&_)+`Brnc&mA3AVbI]%Ee?EZ8jR1DQl)`g:X\!/:0:At:W=]W? -AnN)g\nfaIY,.4,IEA@h53R#NC!n!ME)#'5oCUX\`YVV2iVXt,Uq3g>a#-ACR4GAD6`s*D01DXTHWl.& -WU[&eW0""M/P!Wn[=b/dUPuX`2>#iOD17r7.)kj$cmj)2#3c7'WFT]a_f]VJ"QumY[^*9)&tNXARZ:O@ ->uj6,[6iCm=gjB)[HPWt:!(J>XJ`J;('^lK)3^q]EAsT`l9)a6D\RU#L+!A*4_k[e1$Yc?C;o(paU+b6 -Gbba:c8YW)V:R%ojCO>ooo`e_jZ7&1)MR\q8Rp+md>\5o=2L60l*"<`-lFq&1m\pJ+6R?@jRG6-0KcJ[ -jc4gS-O/Wgijp-Mi,qd* -&<>k!d^)[E4?A?U=`:-3b?kGtSD>E;>OPPp@cMTGR&-`&+gAl3<7O(iW+#0RT$D[U?8*So<8&&"$C[BY -agCjj[fRW0ZkaF/m'N\a>/c8fW:LAF8W5OSBep"4Y\4G":O%@pK*8`pUaWu%"6T3N8RKWL6_XFDZ.qJ+Nluc6IYM=Jpa2md.c"^;A)\l+Uc>(?r -%hQ2??m.EqcTUu6q<#FG!s1Q]1$nZ#`^(IGRZ3rtl1[s_k=r3bI"!)r=jsa6F(L(3cg7/pq$^3n=d;E@ -:!5K`AGo!cd*-H<>Gn:0$._&fX]&Jc(\07Z3GSRD5&Ze)3=6O9F.iqG#.VpVN7N(F'!Wb>k!T#Ff69>QbQ.>?5Ch3AsK -olI1]I^K*S'8S+bKBjN&2Q$YNhqE)@h5&b+) -+Y$;)K(bhmGDU^#+F7K[=;jRA_s'XVCLL=?[h*p7"hb^@JpXele8oLKR.bq)NkG,Pe*a<+Q[Ec=nppN^ -n]&gT)[AQX`r[S0JEA."E;,R<(*'siCGn`3>MJ]L?](1ti6>?Vjhi-&jhjP7+#^!Dn6\dOn@b09^fSRB -&'QQXnfc+LZK20iX13OJ1>#;&nD:B$&"\.77m:%hbFoB_Rhg,%k'-1dJfsu?h!HSTIW,mI=KVpfORMZ& -pGUd4]Y4?b8)& -endstream -endobj -7 0 obj - 28923 -endobj -3 0 obj - << - /Parent null - /Type /Pages - /MediaBox [0.0000 0.0000 431.00 434.00] - /Resources 8 0 R - /Kids [5 0 R] - /Count 1 - >> -endobj -9 0 obj - [/PDF /Text /ImageC] -endobj -10 0 obj - << - /S /Transparency - /CS /DeviceRGB - /I true - /K false - >> -endobj -11 0 obj - << - /Alpha1 - << - /ca 1.0000 - /CA 1.0000 - /BM /Normal - /AIS false - >> - >> -endobj -8 0 obj - << - /ProcSet 9 0 R - /ExtGState 11 0 R - >> -endobj -xref -0 12 -0000000000 65535 f -0000000015 00000 n -0000000315 00000 n -0000029666 00000 n -0000000445 00000 n -0000000521 00000 n -0000000609 00000 n -0000029642 00000 n -0000030120 00000 n -0000029836 00000 n -0000029875 00000 n -0000029977 00000 n -trailer -<< - /Size 12 - /Root 2 0 R - /Info 1 0 R ->> -startxref -30193 -%%EOF diff -Nru ausweisapp2-2.3.1/docs/installation/README.de.rst ausweisapp2-2.4.0/docs/installation/README.de.rst --- ausweisapp2-2.3.1/docs/installation/README.de.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation/README.de.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,494 +0,0 @@ -.. raw:: latex - - \part*{Deutsch} - \addcontentsline{toc}{part}{Deutsch} - -Installation -~~~~~~~~~~~~ - -Windows -------- - -Der Installer der |AppName| kann über die Kommandozeile gestartet werden, um -den Installationsprozess zu konfigurieren und systemweite Standardeinstellungen -vorzugeben. -Der Rückgabewert von msiexec informiert über das Ergebnis der Installation [#msiexecreturnvalues]_. -Neben den üblichen Parametern [#standardarguments]_ enthält das folgende Kommando -alle unterstützten Parameter, die im Anschluss erläutert werden. - -.. code-block:: winbatch - - msiexec /i AusweisApp-X.YY.Z.msi /quiet INSTALLDIR="C:\AusweisApp" SYSTEMSETTINGS=false DESKTOPSHORTCUT=false PROXYSERVICE=false AUTOSTART=false TRAYICON=true AUTOHIDE=false REMINDTOCLOSE=false ASSISTANT=false TRANSPORTPINREMINDER=false CUSTOMPROXYTYPE="HTTP" CUSTOMPROXYHOST="proxy.example.org" CUSTOMPROXYPORT=1337 UPDATECHECK=false ONSCREENKEYBOARD=true SHUFFLESCREENKEYBOARD=true SECURESCREENKEYBOARD=true ENABLECANALLOWED=true SKIPRIGHTSONCANALLOWED=true LAUNCH=true - -INSTALLDIR - Gibt das Installationsverzeichnis an. Ohne Angabe wird der Ordner - "C:\\Programme\\AusweisApp" genutzt. - -SYSTEMSETTINGS - Betrifft die Erstellung von Firewall-Regeln der Windows Firewall. Ohne Angabe - des Parameters werden die Firewall-Regeln erstellt (true). Durch Angabe von - SYSTEMSETTINGS=false werden keine Firewall-Regeln erstellt. - -DESKTOPSHORTCUT - Durch Angabe von DESKTOPSHORTCUT=false kann die Erstellung einer - Desktop-Verknüpfung vermieden werden. Ohne Angabe des Parameters wird eine - Desktop-Verknüpfung für alle Benutzer erstellt (true). - -PROXYSERVICE - Um den parallelen Betrieb mehrer Instanzen der |AppName| zu ermöglichen, ist - der Proxy-Dienst notwendig. Der Proxy-Dienst übernimmt die Überwachung von Port - 24727 (definiert in BSI TR-03124-1) und leitet Anfragen an die lokalen Instanzen - der |AppName| weiter. Eine Weiterleitung der Discovery-Nachrichten (Ergänzung - zu BSI TR-03112-6 - IFD Service - Kapitel 3) erfolgt nicht, so dass SaK-Geräte - in diesem Betriebsmodus nicht erkannt bzw. genutzt werden können. Ohne Angabe des - Parameters wird der Proxy-Dienst automatisch eingerichtet, wenn Terminaldienste - installiert sind und das System im Anwendungsservermodus ausgeführt wird. - -AUTOSTART - Durch Angabe von AUTOSTART=true wird ein Autostart-Eintrag für alle Benutzer - erstellt. - Die Deaktivierung des Autostarts ist den Benutzern in der |AppName| - dadurch nicht möglich. Ohne Angabe wird der Autostart-Eintrag nicht erstellt - (false). In diesem Fall ist es jedoch jedem Benutzer möglich, die - Autostart-Funktion innerhalb der |AppName| für sich zu aktivieren. - -TRAYICON - Aktiviert das Trayicon damit die |AppName| dauerhaft im Hintergrund aktiv ist und jederzeit für - eine Authentisierung zur Verfügung steht. - -AUTOHIDE - Betrifft die automatische Minimierung nach Abschluss einer erfolgreichen - Authentisierung. Ohne Angabe ist diese aktiviert (true). Durch AUTOHIDE=false - wird diese deaktiviert. Der Benutzer kann diese Einstellung anpassen. - -REMINDTOCLOSE - Wenn der Benutzer die |AppName| per Klick auf das X schließt, wird er darauf - hingewiesen, dass nur die Benutzeroberfläche geschlossen wird und die |AppName| - weiterhin im Infobereich zur Verfügung steht (falls das Trayicon aktiviert ist) - bzw. dass die |AppName| geschlossen wird und erneut geöffnet - werden muss um sich gegenüber Diensteanbietern auszuweisen. Zu diesem Zeitpunkt - ist es möglich, den Hinweis zukünftig zu unterdrücken. Durch REMINDTOCLOSE=false - kann dieser Hinweis von vornherein deaktiviert werden. Ohne Angabe ist er - aktiviert (true). - -ASSISTANT - Startet der Benutzer die |AppName| zum ersten Mal, wird die Benutzeroberfläche - geöffnet und ein Einrichtungsassistent angezeigt. Bei jedem weiteren Start wird - die |AppName| im Hintergrund gestartet und der Einrichtungsassistent erscheint - nicht. Durch ASSISTANT=false wird die |AppName| auch beim ersten Start im - Hintergrund ohne Einrichtungsassistenten gestartet. Ohne Angabe ist der - Einrichtungsassistent aktiviert (true). - -TRANSPORTPINREMINDER - Zu Beginn einer Selbstauskunft oder Authentisierung wird der Benutzer einmalig - danach gefragt, ob er die Transport-PIN schon geändert hat. Durch - TRANSPORTPINREMINDER=false kann diese Abfrage deaktiviert werden. Ohne Angabe - ist die Abfrage aktiviert (true). - -CUSTOMPROXYTYPE - Teil der Konfiguration eines Proxys. Gültige Typen sind SOCKS5 und HTTP. - Um einen Proxy zu nutzen müssen alle Parameter gesetzt sein (siehe - CUSTOMPROXYHOST und CUSTOMPROXYPORT). Der Proxy kann nach der Installation - über eine Checkbox in den Einstellungen deaktiviert werden. - -CUSTOMPROXYHOST - Teil der Konfiguration eines Proxys. Angabe des Hosts, unter dem der Proxy zu - erreichen ist. Um einen Proxy zu nutzen müssen alle Parameter gesetzt sein - (siehe CUSTOMPROXYTYPE und CUSTOMPROXYPORT). Der Proxy kann nach der - Installation über eine Checkbox in den Einstellungen deaktiviert werden. - -CUSTOMPROXYPORT - Teil der Konfiguration eines Proxys. Angabe des Proxyports. Nur Werte von - 1 bis 65536 sind gültig. Um einen Proxy zu nutzen müssen alle Parameter - gesetzt sein (siehe CUSTOMPROXYTYPE und CUSTOMPROXYHOST). Der Proxy kann nach - der Installation über eine Checkbox in den Einstellungen deaktiviert werden. - -UPDATECHECK - Wird die Benutzeroberfläche der |AppName| geöffnet, wird eine Überprüfung auf - eine neue Version der |AppName| gestartet, falls seit der letzten Überprüfung - mindestens 24 Stunden vergangen sind. Liegt eine neue Version vor, wird der - Benutzer darüber in einem Dialog informiert. Durch Setzen von UPDATECHECK auf - false oder true kann diese Überprüfung deaktiviert bzw. aktiviert werden. - Die Einstellung kann dann durch den Benutzer in der |AppName| nicht geändert - werden. Ohne Angabe ist die Überprüfung aktiviert, der Benutzer kann die - Einstellung jedoch ändern. Der UPDATECHECK Parameter beeinflusst weder die - Aktualisierung der Anbieterliste noch die Aktualisierung der - Kartenleserinformationen. - -SHUFFLESCREENKEYBOARD - Ist die Bildschirmtastatur aktiviert, können die Zifferntasten zufällig angeordnet werden. - Durch Setzen von SHUFFLESCREENKEYBOARD auf false oder true kann die zufällige Anordnung - deaktiviert bzw. aktiviert werden. Der Benutzer kann diese Einstellung anpassen. - -SECURESCREENKEYBOARD - Ist die Bildschirmtastatur aktiviert, kann die Animation der Zifferntasten deaktiviert - werden. Durch Setzen von SECURESCREENKEYBOARD auf false oder true kann die Animation - aktiviert bzw. deaktiviert werden. Der Benutzer kann diese Einstellung anpassen. - -ENABLECANALLOWED - Aktiviert die Unterstützung für den CAN-Allowed-Modus (Vor-Ort-Auslesen). Wenn ein entsprechendes - Berechtigungszertifikat vorliegt, muss zum Auslesen die CAN anstelle der PIN eingegeben werden. - -SKIPRIGHTSONCANALLOWED - Überspringt die Anzeige des Berechtigungszertifikat im CAN-Allowed-Modus und wechselt direkt zur - CAN-Eingabe. - -LAUNCH - Startet die |AppName| nach dem Ende der Installation. - -Alternativ kann mit Orca [#orca]_ eine MST-Datei erzeugt werden, die die oben -genannten Parameter definiert. Die Parameter sind in den Tabellen "Directory" -und "Property" verfügbar. Übergeben lässt sich die MST-Datei mit dem folgenden -Kommando: - -.. code-block:: winbatch - - msiexec /i AusweisApp-X.YY.Z.msi /quiet TRANSFORMS=file.mst - -Um den Start der |AppName| auf Systemen mit fehlender Grafikbeschleunigung -zu optimieren, kann die Systemvariable "QT_QUICK_BACKEND" auf den Wert -"software" gesetzt werden. In diesem Fall verzichtet die |AppName| auf den -Versuch die Grafikbeschleunigung zu nutzen und startet direkt mit dem -alternativen Softwarerenderer. - -macOS ------ - -Unter macOS ist keine Installation per Kommandozeile vorgesehen. Jedoch können -einige der oben genannten Einstellung durch eine plist-Datei im Verzeichnis -/Library/Preferences systemweit vorgegeben werden. Diese plist-Datei muss dabei -manuell durch den Administrator des Systems hinterlegt werden und wird von allen -(zukünftigen) Installationen der |AppName| verwendet. Alle nicht genannten -Einstellungen werden auf macOS nicht unterstützt. Der Name der Datei muss -"com.governikus.AusweisApp2.plist" lauten. Der Inhalt wird im folgenden -dargestellt: - -.. code-block:: xml - - - - - - trayIcon - - autoCloseWindow - - remindToClose - - showOnboarding - - transportPinReminder - - customProxyType - HTTP - customProxyHost - proxy.example.org - customProxyPort - 1337 - shuffleScreenKeyboard - - visualPrivacy - - enableCanAllowed - - skipRightsOnCanAllowed - - - - -Für die einzelnen Werte gelten die gleichen Beschreibungen wie für die -Windows-Version wobei die Bennennung der Attribute der folgenden Tabelle zu -entnehmen ist. - -======================== ======================= -macOS Windows -======================== ======================= -trayIcon TRAYICON -autoCloseWindow AUTOHIDE -remindToClose [#dialog]_ REMINDTOCLOSE -showOnboarding ASSISTANT -transportPinReminder TRANSPORTPINREMINDER -customProxyType CUSTOMPROXYTYPE -customProxyPort CUSTOMPROXYPORT -customProxyHost CUSTOMPROXYHOST -shuffleScreenKeyboard SHUFFLESCREENKEYBOARD -visualPrivacy SECURESCREENKEYBOARD -enableCanAllowed ENABLECANALLOWED -skipRightsOnCanAllowed SKIPRIGHTSONCANALLOWED -======================== ======================= - -Nach Änderung der Datei kann es notwending sein, ein erneutes Laden der vom -Betriebssystem gecachten Daten zu erzwingen: :code:`killall -u $USER cfprefsd` - -.. [#msiexecreturnvalues] https://docs.microsoft.com/de-de/windows/desktop/msi/error-codes -.. [#standardarguments] https://docs.microsoft.com/de-de/windows/desktop/msi/standard-installer-command-line-options -.. [#orca] https://docs.microsoft.com/de-de/windows/desktop/Msi/orca-exe -.. [#dialog] Unter macOS wird die |AppName| in die Menüleiste minimiert. - - -Anforderungen an die Einsatzumgebung ------------------------------------- - -Rechte für Installation und Ausführung -'''''''''''''''''''''''''''''''''''''' - -Für die Installation der |AppName| sind Administratorrechte erforderlich. - -Die Ausführung der |AppName| erfordert keine Administratorrechte. - - -Verwendete Netzwerk-Ports -''''''''''''''''''''''''' - -In :numref:`porttable_de` werden alle von der |AppName| genutzten Ports -aufgelistet. -Eine schematische Darstellung der einzelnen Verbindungen, die von der -|AppName| genutzt werden, ist in :numref:`communicationmodel_de` dargestellt. - -Die |AppName| startet einen HTTP-Server, der über Port 24727 erreichbar -ist. -Der Server empfängt nur auf der localhost Netzwerkschnittstelle. -Die Erreichbarkeit dieses lokalen Servers ist für die Onlineausweisfunktion -notwendig, da Anbieter mit einem HTTP-Redirect auf den lokalen Server -umleiten um den Ausweisvorgang in der |AppName| fortzuführen (eID1). -Außerdem wird über den Server die Verwendung der |AppName| von anderen -Anwendungen über eine Websocket-Schnittstelle angeboten (SDK-Funktion, eID-SDK). -Daher müssen eingehende lokale Netzwerkverbindungen auf dem TCP Port 24727 -ermöglicht werden. - -Bei aktiviertem Proxy-Dienst übernimmt der |AppName|-Proxy die Serverfunktionen -der |AppName| auf Port 24727. Die Instanzen der |AppName| erkennen den Proxy -und benutzen in diesem Fall einen zufälligen freien Port auf den der Proxy die -Anfragen weiterleitet. - -Für die Verwendung von der "Smartphone als Kartenleser"-Funktion über WLAN -müssen außerdem Broadcasts auf UDP Port 24727 im lokalen Subnetz empfangen -werden können. -Hierzu muss eventuell die AP Isolation im Router deaktiviert werden. - -.. _communicationmodel_de: -.. figure:: CommunicationModel_de.pdf - - Kommunikationsmodell der |AppName| - -Der Installer der |AppName| bietet die Option, für alle angebotenen -Funktionen der |AppName| die erforderlichen Firewall-Regeln in der -Windows-Firewall zu registrieren. -Erfolgt die Registrierung der Firewall-Regeln nicht, wird der Benutzer bei -einem Verbindungsaufbau der |AppName| mit einem Dialog der Windows-Firewall -aufgefordert, die ausgehenden Datenverbindungen zuzulassen. -Durch Registrierung der Firewall-Regeln während der Installation werden diese -Aufforderungen unterbunden. - -Für die lokalen Verbindungen eID1 und eID-SDK müssen (unter den gängigen -Standardeinstellungen der Windows-Firewall) keine Regeln in der -Windows-Firewall eingetragen werden. - -Die durch den Installer angelegten Regeln werden in Tabelle :numref:`firewalltable_de` -aufgelistet. - - -TLS-Verbindungen -'''''''''''''''' - -Es ist generell nicht möglich, die |AppName| mit einem TLS-Termination-Proxy -zu verwenden, da die übertragenen TLS-Zertifikate über eine Verschränkung mit -dem Berechtigungszertifikat aus der Personalausweis-PKI validiert werden. -CA-Zertifikate im Windows-Truststore werden daher ignoriert. - -.. raw:: latex - - \begin{landscape} - -.. _porttable_de: -.. csv-table:: Netzwerkverbindungen der |AppName| - :header: "Referenz", "Protokoll", "Port", "Richtung", "Optional", "Zweck", "Anmerkungen" - :widths: 8, 8, 8, 8, 8, 35, 25 - - "eID1", TCP, 24727 [#aa2proxy]_, "eingehend", "Nein", "Online-Ausweisvorgang, eID-Aktivierung [#TR-03124]_", "Nur erreichbar von localhost [#TR-03124]_" - "eID2", TCP, 443 [#eidports]_, "ausgehend", "Nein", "Online-Ausweisvorgang, Verbindung zum Anbieter, TLS-1-2-Kanal [#TR-03124]_", "TLS-Zertifikate verschränkt mit Berechtigungs-Zertifikat [#TR-03124]_" - "eID3", TCP, 443 [#eidports]_, "ausgehend", "Nein", "Online-Ausweisvorgang, Verbindung zum eID-Server, TLS-2-Kanal [#TR-03124]_", "TLS-Zertifikate verschränkt mit Berechtigungs-Zertifikat [#TR-03124]_" - "eID-SDK", TCP, 24727 [#aa2proxy]_, "eingehend", "Nein", "Verwendung der SDK-Schnittstelle", "Nur erreichbar von localhost [#TR-03124]_" - "SaK1", UDP, 24727 [#aa2proxy]_, "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 Anbietern und Kartenlesern sowie Informationen zu neuen |AppName|-Versionen [#updatecheck]_ .", "Die Zertifikate der TLS-Verbindung werden mit in der |AppName| mitgelieferten CA-Zertifikaten validiert. Im Betriebssystem hinterlegte CA-Zertifikate werden ignoriert." - -.. [#aa2proxy] Oder ein zufälliger Port bei Verwendung des |AppName|-Proxys. -.. [#TR-03124] Siehe TR-03124 des BSI -.. [#eidports] Port 443 wird für die initiale Kontaktaufnahme zum Anbieter bzw. - eID-Server verwendet. Durch die Konfiguration des Dienstes durch den - Diensteanbieter können durch Weiterleitungen beliebige andere Ports zum - Einsatz kommen. -.. [#TR-03112] Siehe TR-03112-6 des BSI -.. [#govurl] Erreichbar unter dem URL https://updates.autentapp.de/ -.. [#updatecheck] Die Überprüfung auf neue |AppName|-Versionen kann deaktiviert werden, siehe - Kommandozeilenparameter UPDATECHECK - -.. _firewalltable_de: -.. csv-table:: Firewallregeln der |AppName| - :header: "Name", "Protokoll", "Port", "Richtung", "Umgesetzte Verbindung" - :widths: 25, 15, 15, 15, 30 - :align: left - - "AusweisApp-Firewall-Rule", TCP, \*, "ausgehend", "eID2, eID3, SaK2, Update" - "AusweisApp-SaC", UDP, 24727, "eingehend", "SaK1" - -.. raw:: latex - - \end{landscape} - -Entwickleroptionen -~~~~~~~~~~~~~~~~~~ - -Die |AppName| verfügt über sogenannte Entwickleroptionen. Diese bieten erweiterte -Einstellmöglichkeiten und unterstützen die Integration eines eID-Dienstes. -Die Entwickleroptionen werden standardmäßig ausgeblendet. - -Aktivieren der Entwickleroptionen ---------------------------------- - -Um die Entwickleroptionen zu aktivieren, öffnen Sie im Menü „Hilfe“ den Punkt -„Information“. Klicken Sie zehnmal auf die „Anwendungsversion“. -Versionsinformationen. Nach dem zehnten Klick erhalten Sie eine Benachrichtigung, -dass die Entwickleroptionen aktiviert sind. Im Bereich Einstellungen befindet -sich nun eine neue Kategorie „Entwickleroptionen“. In den mobilen Versionen -erscheinen zusätzlich Optionen zum "Vor-Ort-Auslesen". - -Außerdem kann in den mobilen Versionen der |AppName| der Testmodus (Test-PKI) -für die Selbstauskunft durch zehn Klicks auf die Lupe im Bereich -"Meine Daten einsehen" aktiviert und deaktiviert werden. - -Erweiterte Einstellungen ------------------------- - -Die Entwickleroptionen bieten erweiterte Einstellungsmöglichkeiten, die -nachfolgend erläutert werden. - -Testmodus für die Selbstauskunft (Test-PKI) -''''''''''''''''''''''''''''''''''''''''''' - -Die Selbstauskunft ist ein fest integrierter Dienst der |AppName| und kann -nur mit Echtausweisen genutzt werden. Wird der Testmodus (Test-PKI) aktiviert, -nutzt die |AppName| einen Test-Dienst, der es ermöglicht, eine Selbstauskunft -mit einem Testausweis durchzuführen. - -Interner Kartensimulator -'''''''''''''''''''''''' - -Der interne Kartensimulator ermöglicht die Durchführung einer Authentisierung in -der Test-PKI ohne Ausweis oder Kartenleser. Beachten Sie, dass in den stationären -Versionen kein anderer Kartenleser verwendet werden kann, während der Simulator -aktiviert ist. - -In der aktuellen Version ist ein einzelnes statisches Profil hinterlegt, das über -die grafische Oberfläche nicht geändert werden kann. Lediglich im SDK ist es -möglich die Daten über das Kommando SET_CARD zu beeinflussen. -Weitere Informationen dazu finden Sie in der Dokumentation des -|AppName| SDK (siehe :ref:`Software Development Kit (SDK) `). - -Entwicklermodus (nur stationär) -''''''''''''''''''''''''''''''' - -Mit der Aktivierung des Entwicklermodus werden einige Sicherheitsabfragen -während einer Authentisierung ignoriert. In Entwicklungsszenarien, in denen -ohnehin mit Test-Diensten gearbeitet wird, führt das Ignorieren der -Sicherheitsabfragen dazu, dass eine Authentisierung erfolgreich durchgeführt -werden kann. Auf jede Sicherheitsverletzung wird in den internen -Benachrichtigungen der |AppName| bzw. des Betriebssystems -hingewiesen. - -Die folgenden Sicherheitsüberprüfungen sind im Entwicklermodus abgeschaltet: - -* Die verwendeten TLS-Schlüssel und ephemeralen TLS-Schlüssel haben die - notwendige Mindestlänge. -* Die URL der Beschreibung des TLS-Zertifikats des eID-Servers und die - TcToken-URL müssen die Same-Origin-Policy erfüllen. -* Die verwendeten TLS-Zertifikate müssen mit dem Berechtigungszertifikat - verschränkt sein. -* Die RefreshAddress-URL und etwaige Redirect-URL müssen das HTTPS-Schema - erfüllen. - -Der Entwicklermodus ist nur unter Windows und macOS verfügbar. - -**Wichtig:** Der Entwicklermodus kann nur für Test-Dienste verwendet werden, -eine Verwendung mit echten Berechtigungszertifikaten ist nicht möglich. - -CAN-Allowed Modus für Vor-Ort-Auslesen untertützen (nur mobil) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Aktiviert die Unterstützung für den CAN-Allowed-Modus (Vor-Ort-Auslesen). Wenn -ein entsprechendes Berechtigungszertifikat vorliegt, muss zum Auslesen die CAN -anstelle der PIN eingegeben werden. - -Anzeige der Berechtigungen überspringen (nur mobil) -''''''''''''''''''''''''''''''''''''''''''''''''''' - -Überspringt die Anzeige des Berechtigungszertifikat im CAN-Allowed-Modus und -wechselt direkt zur CAN-Eingabe. - - -.. _SDK_De: - -Software Development Kit (SDK) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Einsatzmöglichkeiten --------------------- - -Mit dem Software Development Kit (SDK) der |AppName| ist es Ihnen möglich, die -Online-Ausweisfunktion direkt in die eigene Anwendung bzw. App zu integrieren. -Damit ermöglichen Sie Ihren Benutzern die medienbruchfreie Durchführung einer -Authentisierung - z.B. für Registrierungen oder Logins. - -Das SDK bietet Ihnen dabei den Vorteil, die Online-Authentisierung durchgehend im -eigenen Markendesign durchzuführen - ohne dass die Benutzer die gewohnte Umgebung -verlassen müssen. - -Das |AppName| SDK ermöglicht auch die Integration des Vor-Ort-Auslesens. -Hierbei wird anstelle der PIN zur Freigabe der Datenübertragung die CAN -übermittelt. Diese ist auf der Vorderseite des Ausweises aufgedruckt und wird zur -Freigabe des Auslesevorgangs benötigt. - -Integrationsmöglichkeiten -------------------------- - -Bei der voll-integrierten Version des SDKs wird die |AppName| als AAR Package -bzw. Swift Package in Ihre eigene Anwendung eingebunden. -Der Vorteil: Die |AppName| wird direkt mit ausgeliefert, sodass Benutzer die -|AppName| nicht separat auf Ihrem Smartphone installiert haben müssen. - -Bei der teil-integrierten Version des SDKs wird die |AppName| im Hintergrund -aufgerufen. Ggf. kann die App jedoch trotz Teil-Integration mit dem Installer -ausgeliefert werden. - -.. table:: Integrationsmöglichkeiten auf den verschiedenen Platformen - - +-----------------+------------------+------------------+ - | | Teil-Integration | Voll-Integration | - +=================+==================+==================+ - | Windows / macOS | Ja | Nein | - +-----------------+------------------+------------------+ - | Android | Nein | Ja | - +-----------------+------------------+------------------+ - | iOS | Nein | Ja | - +-----------------+------------------+------------------+ - -Entwicklerdokumentation ------------------------ - -Eine ausführliche Entwicklerdokumentation des SDKs und eine Auflistung der -möglichen Fehlercodes finden Sie unter https://www.ausweisapp.bund.de/sdk/. - -SDK Wrapper ------------ - -Sie können den SDK Wrapper der |AppName| zur Vereinfachung der Einbindung -des SDKs in Ihre App verwenden. Der SDK Wrapper bietet Swift und Kotlin -Bindings für iOS und Android an. - -Informationen zur Integration des SDK Wrappers finden Sie in der -Entwicklerdokumentation unter https://www.ausweisapp.bund.de/sdkwrapper/. - -.. raw:: latex - - \newpage diff -Nru ausweisapp2-2.3.1/docs/installation/README.en.rst ausweisapp2-2.4.0/docs/installation/README.en.rst --- ausweisapp2-2.3.1/docs/installation/README.en.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation/README.en.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,455 +0,0 @@ -.. raw:: latex - - \part*{English} - \addcontentsline{toc}{part}{English} - -Installation -~~~~~~~~~~~~ - -Windows -------- - -Start the installer of |AppName| using the command line to configure the -installation process and preset system-wide default settings. -The return value of msiexec indicates the result of the installation [#msiexecreturnvalues]_. -In addition to the usual arguments [#standardarguments]_, the following command -contains all supported arguments, which are explained below. - -.. code-block:: winbatch - - msiexec /i AusweisApp-X.YY.Z.msi /quiet INSTALLDIR="C:\AusweisApp" SYSTEMSETTINGS=false DESKTOPSHORTCUT=false PROXYSERVICE=false AUTOSTART=false TRAYICON=true AUTOHIDE=false REMINDTOCLOSE=false ASSISTANT=false TRANSPORTPINREMINDER=false CUSTOMPROXYTYPE="HTTP" CUSTOMPROXYHOST="proxy.example.org" CUSTOMPROXYPORT=1337 UPDATECHECK=false ONSCREENKEYBOARD=true SHUFFLESCREENKEYBOARD=true SECURESCREENKEYBOARD=true ENABLECANALLOWED=true SKIPRIGHTSONCANALLOWED=true LAUNCH=true - -INSTALLDIR - States the installation directory. If not specified, the folder - "C:\\Program Files\\AusweisApp" is used. - -SYSTEMSETTINGS - Concerns the settings of firewall rules of the Windows Firewall. When not - specifying the argument, firewall rules are created (true). By indicating - SYSTEMSETTINGS=false, no firewall rules are created. - -DESKTOPSHORTCUT - By specifying DESKTOPSHORTCUT=false, no desktop shortcut is created. Without - specifying the argument, the desktop shortcut is created for all users (true). - -PROXYSERVICE - The proxy service is required to enable the parallel operation of several - entities of |AppName|. The proxy service monitors port 24727 (defined in - BSI TR-03124-1) and forwards requests to the local |AppName| instances. - The Discovery messages (amendment to BSI TR-03112-6 - IFD Service - Chapter - 3) are not forwarded, so that SaC devices cannot be recognized or used in - this operating mode. Not specified, the proxy service will be installed - automatically if Terminal Services is installed and the system is running - in application server mode. - -AUTOSTART - By setting AUTOSTART=true, an autostart entry is created for all users. - Users are unable to deactivate the autostart function in the |AppName|. Not - specified, no autostart entry is created (false). In that case, users are able - to activate the autostart function in the |AppName|. - -TRAYICON - Activates the tray icon to keep the |AppName| active in the background to always be available for - an authentication. - -AUTOHIDE - Concerns the automatic minimization after a successful authentication. Not - specified, it is activated (true). Setting AUTOHIDE=false, it is deactivated. - Users can adjust this setting to their preferences. - -REMINDTOCLOSE - Closing the |AppName| by clicking on the X, the user is notified that only the - user interface is closed and that the |AppName| is still available in the info - tray (if the tray icon is enabled) or that the |AppName| will be shut - down and the user needs to restart it to identify towards providers. - At this point, it is possible to prevent future notifications. Setting - REMINDTOCLOSE=false deactivates this notification from the outset. Not - specified, it is activated (true). - -ASSISTANT - Starting the |AppName| for the first time, the user interface is displayed and - the installation wizard is shown. With each subsequent start, the |AppName| - is started in the background, without the installation wizard being shown. By - indicating ASSISTANT=false, the |AppName| is started in the background without - the installation wizard from the outset. Not specified, the installation - wizard is activated (true). - -TRANSPORTPINREMINDER - Prior to the first authentication, the user is asked once whether they have - changed their Transport PIN. Setting TRANSPORTPINREMINDER=false deactivates this - reminder. Not specified, the reminder is activated (true). - -CUSTOMPROXYTYPE - Part of a proxy configuration. Valid values are SOCKS5 and HTTP. - All proxy parameters have to be set to use the proxy (see - CUSTOMPROXYHOST and CUSTOMPROXYPORT). You can disable the proxy after installation - with a checkbox in the settings. - -CUSTOMPROXYHOST - Part of a proxy configuration. Sets the Host of the proxy. All proxy parameters have - to be set to use the proxy (see CUSTOMPROXYTYPE and CUSTOMPROXYPORT). - You can disable the proxy after installation with a checkbox in the settings. - -CUSTOMPROXYPORT - Part of a proxy configuration. Sets the port of the proxy. Only values between - 1 and 65536 are valid. All proxy parameters have to be set to use the proxy (see - CUSTOMPROXYTYPE and CUSTOMPROXYHOST). You can disable the proxy after installation - with a checkbox in the settings. - -UPDATECHECK - Upon opening the user interface of the |AppName|, an update check is started, - provided that at least 24 hours have elapsed since the last update check. If a - newer version is available, the user is notified accordingly. Setting - UPDATECHECK to false or true deactivates or activates the update check - respectively. Users are unable to change this setting in the |AppName|. Not - specified, the update check is activated, but users can adjust the settings. - The UPDATECHECK parameter affects neither updates of the service - provider list nor updates of card reader information. - -SHUFFLESCREENKEYBOARD - If the on-screen keyboard is activated, the number keys can be arranged at random. - By setting SHUFFLESCREENKEYBOARD to false or true, the random arrangement can be - deactivated or activated. Users are able to adjust the setting. - -SECURESCREENKEYBOARD - If the on-screen keyboard is activated, the animation of the number keys can be - disabled. By setting SECURESCREENKEYBOARD to false or true, the animation can be - activated or deactivated. Users are able to adjust the setting. - -ENABLECANALLOWED - Enables support for the CAN allowed mode. If the provider got issued a corresponding authorization - certificate the ID card can be read by entering the CAN instead of the PIN. - -SKIPRIGHTSONCANALLOWED - Skips the page with the authorization certificate in the CAN allowed mode and asks directly for - the CAN. - -LAUNCH - Starts the |AppName| after the installation has finished. - -Alternatively, Orca [#orca]_ can be used to create an MST file that defines the -above arguments. The arguments are available in the "Directory" and "Property" -tables. The MST file can be transferred with the following command: - -.. code-block:: winbatch - - msiexec /i AusweisApp-X.YY.Z.msi /quiet TRANSFORMS=file.mst - -In order to optimize the start of the |AppName| on systems with no graphics -acceleration, the system variable "QT_QUICK_BACKEND" can be set to the value -"software". In this case, the |AppName| does not attempt to use graphics -acceleration and starts directly with the alternative software renderer. - -macOS ------ - -MacOS does not provide a command line installation. However, some of the above -settings can be specified system-wide by a plist file in the -/Library/Preferences directory. This plist file must be manually stored by the -administrator of the system and will be used by all (future) installations of -|AppName|. All not mentioned settings are not supported on macOS. The name of -the file must be "com.governikus.AusweisApp2.plist". The content is shown below: - -.. code-block:: xml - - - - - - trayIcon - - autoCloseWindow - - remindToClose - - showOnboarding - - transportPinReminder - - customProxyType - HTTP - customProxyHost - proxy.example.org - customProxyPort - 1337 - shuffleScreenKeyboard - - visualPrivacy - - enableCanAllowed - - skipRightsOnCanAllowed - - - - -The description for each value is applicable for both Windows and macOS, -although the naming of the attributes differs, as shown in the following table: - -======================== ======================= -macOS Windows -======================== ======================= -trayIcon TRAYICON -autoCloseWindow AUTOHIDE -remindToClose [#dialog]_ REMINDTOCLOSE -showOnboarding ASSISTANT -transportPinReminder TRANSPORTPINREMINDER -customProxyType CUSTOMPROXYTYPE -customProxyPort CUSTOMPROXYPORT -customProxyHost CUSTOMPROXYHOST -shuffleScreenKeyboard SHUFFLESCREENKEYBOARD -visualPrivacy SECURESCREENKEYBOARD -enableCanAllowed ENABLECANALLOWED -skipRightsOnCanAllowed SKIPRIGHTSONCANALLOWED -======================== ======================= - -It might be necessary to force a reload of the data cached by the operating -system: :code:`killall -u $USER cfprefsd` - -.. [#msiexecreturnvalues] https://docs.microsoft.com/en-us/windows/desktop/msi/error-codes -.. [#standardarguments] https://docs.microsoft.com/en-us/windows/desktop/msi/standard-installer-command-line-options -.. [#orca] https://docs.microsoft.com/en-us/windows/desktop/Msi/orca-exe -.. [#dialog] On macOS the |AppName| is minimized to the menu bar. - - -Operational Environment Requirements ------------------------------------- - -Required authorization for installation and execution -''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Administrator privileges are required to install the |AppName|. - -The execution of the |AppName| does not require administrator privileges. - -Used network ports -'''''''''''''''''' - -All network ports used by the |AppName| are listed in :numref:`porttable_en`. -:numref:`communicationmodel_en` shows a schematic representation of the -individual connections made by the |AppName|. - -The |AppName| starts a HTTP-Server on port 24727. -The server binds only to the localhost network interface. -The availability of the local server is necessary for the online eID function, -because providers will redirect the user with a HTTP redirect to the -local server to continue the authentication process in the |AppName| (eID1). -The server is also used to offer other local applications to use the -|AppName| via a websocket interface (SDK function, eID-SDK). -Therefore local incoming network connections to TCP Port 24727 must be -permitted. - -If the proxy service is activated, the |AppName| proxy takes over the server -functions of |AppName| on port 24727. The entities of |AppName| recognize -the proxy and use a free random port in this case to which the proxy forwards -the requests. - -Broadcast on UDP port 24727 in the local subnet have to be receivable by the -|AppName| to use the "Smartphone as Card Reader" functionality. -It may be necessary to deactivate AP isolation on your router. - -.. _communicationmodel_en: -.. figure:: CommunicationModel_en.pdf - - Communication model of the |AppName| - -The installer of the |AppName| provides an option to register all needed -firewall rules in the Windows Firewall. -If the rules are not registered, the user will be prompted by the Windows -Firewall to allow the outgoing connections once the |AppName| tries to -connect to a server. -These prompts are suppressed by registering the firewall rules during -installation. -No rules have to be added to the Windows Firewall for the local connections -eID1 and eID-SDK (when using the standard settings). - -In table :numref:`firewalltable_en` all firewall rules registered by the -installer are listed. - -TLS connections -''''''''''''''' - -Transmitted TLS certificates are solely validated via the interlacing with -the authorization certificate issued by the german eID PKI. -CA certificates in the Windows truststore are thus ignored. -It is therefore generally not possible to use the |AppName| behind a -TLS termination proxy. - -.. raw:: latex - - \begin{landscape} - -.. _porttable_en: -.. csv-table:: Network connections of the |AppName| - :header: "Reference", "Protocol", "Port", "Direction", "Optional", "Purpose", "Note" - :widths: 8, 8, 8, 8, 8, 35, 25 - - "eID1", TCP, 24727 [#aa2proxy]_, "incoming", "no", "Online eID function, eID activation [#TR-03124]_", "Only accessible from localhost [#TR-03124]_" - "eID2", TCP, 443 [#eidports]_, "outgoing", "no", "Online eID function, connection to the provider, TLS-1-2 channel [#TR-03124]_", "TLS certificates interlaced with authorization certificate [#TR-03124]_" - "eID3", TCP, 443 [#eidports]_, "outgoing", "no", "Online eID function, connection to eID-Server, TLS-2 channel [#TR-03124]_", "TLS certificates interlaced with authorization certificate [#TR-03124]_" - "eID-SDK", TCP, 24727 [#aa2proxy]_, "incoming", "no", "Usage of the SDK functionality", "Only accessible from localhost [#TR-03124]_" - "SaC1", UDP, 24727 [#aa2proxy]_, "incoming", "yes", "Smartphone as Card Reader, detection [#TR-03112]_", "Broadcasts" - "SaC2", TCP, , "outgoing", "yes", "Smartphone as Card Reader, usage [#TR-03112]_", "Connection in local subnet" - "Update", TCP, 443, "outgoing", "yes", "Updates [#govurl]_ of provider and card reader information as well as information on new |AppName| versions [#updatecheck]_ .", "TLS certificates will be validated against CA certificates included in the |AppName|. CA certificates provided by the OS are ignored." - -.. [#aa2proxy] Or a random port when using |AppName| proxy. -.. [#TR-03124] See TR-03124 specification from the BSI -.. [#eidports] Port 443 is used for the initial contact with the provider or - eID server. Due to configuration of the service on the service provider's - behalf, any other port might be used by forwarding. -.. [#TR-03112] See TR-03112-6 specifiaction from the BSI -.. [#govurl] All updates are based on the URL https://updates.autentapp.de/ -.. [#updatecheck] Automatic checks for new |AppName| versions can be deactivated, see commandline parameter - UPDATECHECK. - -.. _firewalltable_en: -.. csv-table:: Firewall rules of the |AppName| - :header: "Name", "Protocol", "Port", "Direction", "Connection reference" - :widths: 25, 15, 15, 15, 30 - :align: left - - "AusweisApp-Firewall-Rule", TCP, \*, "outgoing", "eID2, eID3, SaC2, Update" - "AusweisApp-SaC", UDP, 24727, "incoming", "SaC1" - -.. raw:: latex - - \end{landscape} - -Developer Options -~~~~~~~~~~~~~~~~~ - -|AppName| features so-called developer options. They provide advanced settings and -facilitate the integration of eID services. -The developer options are hidden by default. - -Activating the Developer Options --------------------------------- - -Developer options are activated by clicking the "Application Version" accessible -via "Help" -> "Information" 10 times. After the 10th time, you will receive a -notification that the developer options are activated. Once activated, you will -find a new category "developer options" in the settings menu. In the mobile -versions additional options for "on-site reading" appear. - -In the mobile versions of |AppName| you can also activate and deactivate the test -mode (Test PKI) for self-authentication by clicking the magnifying glass on the -start screen 10 times. - -Advanced Settings ------------------ - -The developer options offer advanced settings, which are explained below. - -Test mode for self-authentication (Test PKI) -'''''''''''''''''''''''''''''''''''''''''''' - -In general, the self-authentication is a built-in service of |AppName| and -can only be used with genuine ID cards. However, when in test mode, |AppName| -uses a test service allowing for self-authentication with a test ID card. - -Internal card Simulator -''''''''''''''''''''''' - -The internal card simulator allows to run an authentication in the Test PKI -without any ID card or card reader. Note that no other card reader can be used in -the stationary versions while the simulator is activated. - -A single static profile is stored in the current version, which cannot be changed -via the graphical user interface. Only the SDK allows to change the profile's data -using the SET_CARD command. Further information can be found at the documentation -of |AppName| SDK (see :ref:`Software Development Kit (SDK) `). - -Developer mode (stationary only) -'''''''''''''''''''''''''''''''' - -When the developer mode is activated, some safety measures during an -authentication process are ignored. Ignoring the safety measures with test -services usually employed in test scenarios, yields a successful authentication. -Each safety breach will be highlighted as an internal notification in |AppName| -or the operating system respectively. - -The following safety tests are disabled in the developer mode: - -* The used TLS keys and ephemeral TLS keys have the necessary minimum length. -* The URL of the TLS certificate description of the eID server and the TcToken URL - must fulfill the same-origin policy. -* The used TLS certificates must be entwined with the authorization certificate. -* The RefreshAddress URL and possible redirect URLs must conform to the HTTPS - scheme. - -**Please note:** -Developer mode can only be used for test services, usage with genuine provider -certificates is not possible. - -Support CAN Allowed mode for on-site reading (mobile only) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Enables support for the CAN allowed mode. If the provider got issued a -corresponding authorization certificate the ID card can be read by entering the -CAN instead of the PIN. - -Skip rights page -'''''''''''''''' - -Skips the page with the authorization certificate in the CAN allowed mode and asks -directly for the CAN. - -.. _SDK_En: - -Software Development Kit (SDK) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Possible Uses -------------- - -The software development kit (SDK) of |AppName| enables you to integrate the eID -function directly into your own application or app. This enables users to -authenticate themselves without media discontinuity. - -The SDK offers the advantage of being able to carry out an online -authentication in your own brand design - without users having to leave the -familiar environment. - -The |AppName| SDK also enables the integration of on-site reading. In this case, -the CAN is transmitted instead of the PIN to enable data transmission. You find -the CAN on the front of the ID card and you need it to enable the readout process. - -Integration Options -------------------- - -With the fully integrated version of the SDK, |AppName| is integrated into your -own application as an AAR package or Swift package. The advantage: |AppName| is -delivered directly with the application so that users don't have to install -|AppName| separately on their smartphone. - -With the partially integrated version of the SDK, |AppName| is called in the -background. Where applicable, however, the app can be delivered with the installer -regardless of partial integration. - -.. table:: Integration options for the different platforms - - +-----------------+----------------------+------------------+ - | | partially integrated | fully integrated | - +=================+======================+==================+ - | Windows / macOS | Ja | Nein | - +-----------------+----------------------+------------------+ - | Android | Nein | Ja | - +-----------------+----------------------+------------------+ - | iOS | Nein | Ja | - +-----------------+----------------------+------------------+ - -Developer documentation ------------------------ - -You can find a detailed developer documentation of the SDK with a list of possible -failure codes at https://www.ausweisapp.bund.de/sdk/. - -SDK Wrapper ------------ - -You can use the SDK Wrapper of the |AppName| to simplify the integration of -the SDK into your app. The SDK Wrapper offers Swift and Kotlin bindings for iOS -and Android. - -You can find information for integrating the SDK Wrapper in the developer -documentation at https://www.ausweisapp.bund.de/sdkwrapper/. diff -Nru ausweisapp2-2.3.1/docs/installation/conf.py.in ausweisapp2-2.4.0/docs/installation/conf.py.in --- ausweisapp2-2.3.1/docs/installation/conf.py.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation/conf.py.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,180 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import sys -import os -import shlex - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.4' - -# If true, figures, tables and code-blocks are automatically numbered -# if they has caption. For now, it works only with the HTML builder. -# Default is False. -numfig = True - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [] - -locale_dirs = ['@SPHINX_DOCS_DIR@/locales/'] - -gettext_additional_targets = ['image'] -gettext_location = False -gettext_compact = True - -# Add any paths that contain templates here, relative to this directory. -#templates_path = ['@SPHINX_DOCS_DIR@/_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = '@PROJECT_NAME@ Erweiterte Dokumentation für Administratoren und Entwickler' -copyright = '2018-2025, Governikus GmbH & Co. KG' -author = 'Governikus GmbH & Co. KG' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '@PROJECT_VERSION@' -# The full version, including alpha/beta/rc tags. -release = '@VERSION_DVCS@' - -today = ' ' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = '@SPHINX_LANG@' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -#exclude_patterns = [''] - - - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -html_favicon = '@SPHINX_DOCS_DIR@/../../resources/images/desktop/npa.ico' - -#html_theme_path = ['@SPHINX_DOCS_DIR@/_themes'] - -#html_theme = 'appcast' -html_theme = 'sphinx_rtd_theme' - -# If false, no module index is generated. -html_domain_indices = True - -# If false, no index is generated. -html_use_index = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -html_show_sphinx = False - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -html_show_copyright = True - -html_scaled_image_link = False - -# Output file base name for HTML help builder. -htmlhelp_basename = '@PROJECT_NAME@InstallationIntegration' - -html_context = { - 'display_github': False, - 'display_bitbucket': False, - 'show_source': False, - 'html_show_sourcelink': False, -} - -html_permalinks = True - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -'papersize': 'a4paper', - -# The font size ('10pt', '11pt' or '12pt'). -'pointsize': '11pt', - -# Additional stuff for the LaTeX preamble. -'preamble': ''' -\\usepackage{lscape} -\\hypersetup{pdfauthor={Governikus GmbH \& Co. KG}, - pdftitle={@PROJECT_NAME@}, - pdfsubject={Erweiterte Dokumentation für Administratoren und Entwickler}, - pdfkeywords={installation, integration}, - pdfproducer={LaTeX}, - pdfcreator={Sphinx} -} -''', - -# Override tableofcontents -'tableofcontents': ''' -% Remove header of tableofcontent -\\makeatletter -\\@starttoc{toc} -\\makeatother - -\\newpage -\\pagestyle{plain} -\\pagenumbering{arabic} -''', - -# Latex figure (float) alignment -'figure_align': 'H', -'sphinxsetup' : 'HeaderFamily=\\linespread{1.9}\\sffamily\\bfseries', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, '@PROJECT_NAME@-@VERSION_DVCS@-NetInstallation_Integration.tex', '@PROJECT_NAME@ \\linebreak Erweiterte Dokumentation für Administratoren und Entwickler', - 'Governikus GmbH \& Co. KG', 'howto'), -] - -# 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/npa.png' - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -latex_show_urls = 'footnote' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - -rst_epilog = """ -.. |AppName| replace:: @PROJECT_NAME@ -""" diff -Nru ausweisapp2-2.3.1/docs/installation/index.rst ausweisapp2-2.4.0/docs/installation/index.rst --- ausweisapp2-2.3.1/docs/installation/index.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation/index.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -Table of contents ------------------ - -.. raw:: latex - - \clearpage - -.. toctree:: - :maxdepth: 5 - - README.de - README.en diff -Nru ausweisapp2-2.3.1/docs/installation_de/CommunicationModel_de.graphml ausweisapp2-2.4.0/docs/installation_de/CommunicationModel_de.graphml --- ausweisapp2-2.3.1/docs/installation_de/CommunicationModel_de.graphml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_de/CommunicationModel_de.graphml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + Internet + + + + + + + + + + + + + + + + + + + Lokales Netz + + + + + + + + + + + Lokaler Rechner + + + + + + + + + + + AusweisApp + + + + + + + + + + + eID-Server + + + + + + + + + + + Anbieter + + + + + + + + + + + Browser + + + + + + + + + + + Update-Server + + + + + + + + + + + Mobiles Endgerät + + + + + + + + + + + Drittanwendung + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eID1 + + + + + + + + + + + eID3 + + + + + + + + + + + + + SaK1, +SaK2 + + + + + + + + + + + Update + + + + + + + + + + + eID2 + + + + + + + + + + + eID-SDK + + + + + + + + + diff -Nru ausweisapp2-2.3.1/docs/installation_de/CommunicationModel_de.pdf ausweisapp2-2.4.0/docs/installation_de/CommunicationModel_de.pdf --- ausweisapp2-2.3.1/docs/installation_de/CommunicationModel_de.pdf 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_de/CommunicationModel_de.pdf 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,463 @@ +%PDF-1.4 +%âãÏÓ +1 0 obj + << + /Title () + /Author () + /Subject () + /Keywords () + /Creator (yExport 1.5) + /Producer (org.freehep.graphicsio.pdf.YPDFGraphics2D 1.5) + /CreationDate (D:20230821114836+02'00') + /ModDate (D:20230821114836+02'00') + /Trapped /False + >> +endobj +2 0 obj + << + /Type /Catalog + /Pages 3 0 R + /ViewerPreferences 4 0 R + /OpenAction [5 0 R /Fit] + >> +endobj +4 0 obj + << + /FitWindow true + /CenterWindow false + >> +endobj +5 0 obj + << + /Parent 3 0 R + /Type /Page + /Contents 6 0 R + >> +endobj +6 0 obj + << + /Length 7 0 R + /Filter [/ASCII85Decode /FlateDecode] + >> +stream +Gb"07bDp/UYoPgMYK.en[(d\MN>cK'#OFP!\.O6jK*XbErVo.[>0G-;T87#T"Vr`oJK.X0L$u@W5KrNV +B'/n@ScAX^0E:qr^VEn)q*.+$5C`V.O4OV$J$b)G*Ig&WT7$Q]IDs6fhgYLd+91`@kPti3sg.jZdR\^hAXFL8i +;_?8'h1+S)l0A77m=05)Vk="@TtAkdqS.O0\"%4s07Fk>JBihogm1+>>Mj1rlPm:q0eEK#q07PNq,Zdt +9[:+-c:i]+l`J\u%UO'rp2o#8X6Jerb9c>(d[p&C7^&;VpE'Y7hB/kHUUAOOrCHfZ +j$%N]Vk`u.VaGk@a*:#SlM..!gZcU*m6#1ZYnDgmpXo8aD!S1@_3f8\)/I=Vqas,%EudokgC>k-r40K- +`+O/V5\IY0SOrBbJJ\=?0cTefVYptEMVI`0n\@gpIeC0jB(PYDQ?i"KI[m"=DtncWc[WnVJ6F&iN+SSg +oL#8jBWX)!nQ%s%6LT/CgZd*H%[R5XDa%iOcH8abcOpJJN43+Rs'ot0?##8MIp&98ZWcHarLn=,efJJ3 +b3>MbXI=X)r&/3^k79&=hd;`B'*+H[3^#o^\)@j`iQcG%_7oQphpJ%Z0VTB7USQ +Q+kUK'BXSi7^NM'4b7UFBD5gPt#PHMN!ZeXo]+X>kVKioC +5R5gt>QPcM$"Q"bFl&C-reZOh'LE6,BoJ/#F4,K"?N=HCIP;tKLm;U[u`k0 ++"Pa]>NtPG[!)/q9$Yo>i^nB3_\ +V]E.a\id)mj>p);&BE81:n.L:9X3sY''Tj"=T;I/"f/Z5ra0SX.%cMXM)T6%f2AO'q7d +I),aNa*RC>/Jr0s3`,^kHMu,BrpVk$qF;"o(e?s55OVPf%HRE9Ii\=UpZg%j@K#&7``L;KJ#pJY +3s=)2P`BY=rbR5e.MeY:?OZiW]>*@C3l(,kY*WJCGPj-^Mi]4o.Ld?eIF'[= +lPkSA=(p)'PA39\I[FLi/97R+-WPBNA0V*4D%/<;[VhFX,a," +3R%4MZS]o#G3!J@i'F3/@tl<_4YULlHBj?A2-7DoQ,_/7IMr`];UMhh7hIqR^d1=<1=+>:Q!pQh<\;VR3r/j=>W08?5&`\O&2af4@6fb?a"d(qTiBK\-pe"gc$np>N3]GqIO +1!JuGm;6I8B>&5r\^AWbEB:%g?ISQq5W*`Xl!`t;^HB\ad%FNg5aLZ'q!?^mL+RYs&mmA(A*(&X:rb>c +b3$4\We/'WUl74+,pra*J%J0uZu3FGn5"TafX9"0b9Mctkm>PZn'D;^(A2?N0WmnoA`uKW&,M`-ShB]P +.(PT<6FZEXA?6g\MEsasiUd>o:58jp.<&J-WGKUf'mf";)20IT;AJOJ;VA-.c#VfYGIUlf#<\LQ,3\WG +[`]hX2X,A*D1ru?C992;J*1m?l6)egPukn:`1S'h!nVu\cK&,]G6244,i*-kiDOnJh%j^D?O%#bD]gr@0?tM&U[n\GDhF< +1R;8e7PNu9L(SfF1SldW#G2GMR)X:,K*KRB)Sm.`KR&PbR.P[8&UY.+-8U5_Kb7#VE9*QRdC=J[21$1V +SX[sLg^;`>Xq=nd7@gnSFtYe+IB=P[nG1L'f*FRdA#G"mCtMHb\A*?W\`7crN*\MeqJC)6$3&,7n%?hh +@[`Ds_3o0p2oFQ]O9@TdF@3gk?UmK@%0)c_9_BUd!^VGtm4]:FSK>)(%YqT4CN\;m2j$]%UV>dA6].R+[)gj%q3rEqSF2S>cI,LYh +aa_OlJW]+@$.?)mE.K8QjS^b@I'n^@'D(^X+IJ!'H6?Gpe-\!+nTO[3hZGe>*=IRidF"PK*!O-T*7M0# +#T%>9tl,]7sDT/E/CI^!8FL+6(>nu%:07?o>D+XSWXB@iNm&]RS"NB\\$o88;Jf5G1@K +4sS>5IH:>/$X,1O+)jeakP`;hg#W*dK(fAjJZDp:]qC;TJY?Zomm"M&6&TaW!MZW]pE/aF#?oGa9)et] +pU.TF+/:Lfo)=1\P:uA`C`FGTVmDcG*OCsljo#9O@(0-jMZ<8/48eYq;Z?KtNb+^rc5b"9KEH<)nu6SY +*aH\?@5nZWG*E*YOZPf;LVn*O*P/[t3:%u=r[SH`k^Xnl?O*Ogqu!=?obHBQl1qMB*2&>R!'P#(k*##R +RB<@IfN=gNW-+s'%T%OS?46"NNd^*M\#/g=lfb"qj1+c)D6R2"Ke+PmEa5iA#]XP5feKKUU\H@R+VAa= +J&V0X/5@5SmsZhdMD$(J?dV<3qq6S)s7!Y8pcH0BB*%`OKEnZaZeRJ`a8So>dZ&]Aro,I-dpfVsodJik +kkP8V_]S3lQa^@qlg$h'&+0$O^\G_(MH2ddot98V+&)[;rm%4k5UJPkH)Gt'+Q'*rlbK.-2hHDtf'b2( +HtE6`s%Z8=+!\%?e3uH,m/G3L9$tCY[/Y0orl[b-0,O#j>dXNOdJhInLsPEJ*MhtWd: +)>Y(&:(_i0?JHAC +oPeo5]sRc/m%rZR`8;p;hAHp>/i?5r-\g7(E'N*3mK4dV*-D1+:_QpoL['q&m#Zh^=[]@#T8H?=@deau4bX+FSo&0YBGXG*H0h74*/]24k +[6TIeOc(7IYSD(7/8tdOG>KT^n^Gta7PRH_iK9>'=_%"Waf2\ +&\OBhF,7q8U*#HB!cQ1mrfWf*hch0-+Tm([.uFE:o]X;l_s+]Q]&Z]3\@54S@qMP$^k#!Gos"n&*/06U +=N5N^ULKmiCZ8'@o8"gXK3AfXNd5oYG/aoBn&!b(FFf<0*c6)nIn]9V3bB=@l-Gr*LERQC>Kef1n]pr: +geSS9m`tfPI]X,S@O`WcoZ.O:nD;XO[2ckiCuE(bH6c;DmMS"UOJT'9>0;jUKsBcOMRT?Vj5odiKjI;9 +>LmiUJI.)I2X*s3L-ml9aiUV-d_[O^Qar]od_Qj&IILLHg(XCR;A`:+#BfSW0#_mU3AeVnm3Kh^+c8hB$47R"aVo9"*<2Iq0q6VR!j]2#^f@jNsMG9rQq:&@O6o^@nQ +5mM"FBtWRa-g[LLZQi3uLZ_F:[I[MTV"'$GpW/+lLZWC56`e9b0kp!qV6OHm`iGM4Ubk:(9mL)[]O7X+ +5/c\_8TE[4qO:*uk4@qu4c.`Gg[n-9)D<)MZn%+PA6!\bN*31*V6Q_U`k^[ZI@"rg#OX,5+\j-Fc/Ld4 +)RWpd]YNJ-+#HpW6hjhjBoF^)H):%VrfN-3iRh]+q:;QS=e's-p;i"K=LsAn43tP;QS774QT*.W#;7qS +4*DF3"Fe=pK+[ES@gDHpIE)!EDXc33*P:'+F!s23,9r',J"ouF`EJ(B2%+$MkL+qq9BONC\uiB6Xcj'6hNo&7oE`UQ(QjQ5%P+0#&)Vt]T9 +?J"M>UD]d!RfslhIGl-:WIk`!@fGiLMT@2I43oE839^1]TlBk"2e&-aL>Rg%J;a;]-Y`_8o>'AqhP\.N +n`Vl*_(?79%/ondU_%N":[* +ID1>)QJLF8k^c!aMZuA"B+;b&cTYbVrrO9+rWU3<#F^$a;P1#(>J/9V?Z'W?HqoZ/TbN8*4M06KDacuC +&I.7dG)!F#XC:F)8N?M@"cej-XN1@h@;?t8>LZ#F'&4g[^(V6`"1@L2#ijq!mHN*Knrn!,51f_XG4a)K +pA/T2)WSmDOmk=loKXe6R5B8]XcPl:fm46s?-aEQ]UNifRPLo^nJL$f;gI7pZuZ/YXoF.7]>`uaO:c]a +GpN:>KsP@qg,amo+^1?-QL$]P-u![-\@[h3W]gk^R[7#lld/]CRRI'"nfR\oe3"K5m(e\e`'_-Ba%i)` +WBP3JG<<+@kdRsVaq2;^7)R&TGW2b2k,E$SR3U?LfbtBBn8OP!IDWulr,T\)N@o2,iF]m)&j +mF@(M$`)C4b71%]S3IPOj+J*aeXgI-*["bH3)MJeP8tW2W^k\DNk4U@@*u"!qURbl0[?>qWf;FC2D^hNSAil;kJ*hIat6.:Wp_j> +RZ&<^)isWb+56#1iV;GB6o4F=>B>L>5TKZ%7p8WN%qnD(Hi3Hr48a,E;VpTU:744(qZVFFT)BjA2=;Dg +;t+.A`Hn+oW(HUGnquN80"#Sp<`1]1-p6RUA!^9r[,b5p\WB4Z8UJ>9'rn!6Bnjbr-0/&p;"d7_iJ;<' +]W+GPP#mZK?!Q=#Ei.%!bhb@doI\VuPguP#oW02T[I>0!?nQR[LiU?5:p<>[E\tr_$RgYLXC# +$%30FV>`j6o@=ne%Hm7M^N=aTEX;%UY8k0e@d/]1=*X(ssh`g#^aQ_oH83CKC$8H%gBY +%sc,L$kIV`D?$s[d0t;SiYM21YH%++afATN%R;4D,^JKRnd<-*+g7p5@$asO#K\L?bN7&'qa$Kd[^:=d +#7:q@[3en[4If?<4]$+e(Am`fDa;[,li6`KDGQ3h*"RmY1*5h>-RDOSi[PBqE&3F6nqBQ!P7&2e1N7:G +`4ac0i%!Qp1m=W'3$@Wpd:>$SiY:,o4r^WIFXTeW[R-&8mQ?jNM,U5!\nR0i,-6^6Cl?(2!XQQJI=8OX +:c3YH=W`rA&9QE4JCoaR?pK03>a00:&f2cM*?9K@%k[W@k!em?RC+DsESREF]1`<[mgcH>?S4!MS(cWJ +3^qlok[2iER;>:"k9aE&emhd@W&W4]k.Ig%e5]jF6rQDeHICQN&)&]W[*Zpl^14&M*Z##toGSY@=]$/> +#,NHbK%iCk]fVI;@#4&!+13as7eB\2IQ*Q_]<*Ob\2 +(CV(@21GV.!u@+Y4!(A@F/k(Xb0`VYR^FSnKD'@ +q"`Pl*kIUNW68kqsNJtTASUo;'%/l/]+bcrRP'ij;Yg:5\$D@o*rS2(R#j'il,%7 +#$O>$#R.G]_K&2I=2Qe*L3'lH)&XUS!AV[L$O +[+RS/d%>lT-j`JLNf_q/cPf8D)o@Yp*pbE2]rDQ$jAWCUN_gg;P-33^j[`n#S%M`bh*fja6P,4H#`ViO +[;S(V)K]W`UfL#MbgUO,F\VP;B$Gh"j9'8`QV&YJ=^!b>J^M"sQChjS^":,=HrH2h$N#.:F]u^0p`8=e +B:!5&.ok)SC,0m1pC?q3A"Dc$h4,A;5?4YO($tPQg-3O17DiDHe_S7u)Rt%V9t#,YV%,7VfBuE&k.=TR +pL?]WpFA8`bZY&!9P00'-rabN)P;,4Z/t4kc"&A&PdO9i2k@#`fSu>AqDR.ff`R@6`uh:Q=b_U +b(J:;?K7U)9La8ITp^iSQ:PV;;tqg3m$:D>gJ$1[B8$87b/568`?/lY1 +ZV;A8BW"SO4iVY\;4>U#0lQ?BYI&^Za>K/9h4H1AA1%'G8i(HDY%&2T&GcKYZS77'GTtaP(#"20aS`G^^T`<1\G\/O+m' +hIX2em*91?2njLfd+>D-9*`l$?-U\8[-8>BNf_H2,7n5""Mr41Y\?-`b5i>01-!#AW>7f`-X4kZ^=)R[at*H/NkTdAB*nr+q#T^!=U +h[3&0e"2=P^1i3&DM?(\lGV&p5jHI=@S(?br\n"\aD6OS\JM@m$$ +rO3TAFG?Hndi0+*X<7ib#K:^Efd9tii`5Oc6Hr,^Xc*iD/`hmk@:lCq3t#=ATO71X>kl&.%($fCEnUs7 +NC___>8hNpC_Li^O<`8s^>Z(EZKsS17'bpBIq5\&#Q2^'(%nKL)\1Y&\k2\-&tM-Rt0"&Pa"f%tQqbN+J/Dk5BIOhUtP +G5D3ZX9S_faUphj5V/ds.dU.Y3YC2rU/05ue'P)G`CjMGVLSbO\Moc5K!gg`WK7#*ROL6(00Sj5.]e!Z +ee@dp8'7U.-YC7-IlLcU#LS?q)*'](bYE.g[W&j\8Do>3^3iH%;h%_C"g%YMZ0k+*/PM^2WH92CW6"/& +fpIm7#.mO=27RhJdGZh6IDWFf5)Ag6YjW0RrY5[WdjDpAQo!?P+3u_Gj#`:ig%2D7[5jg\odkXI#X1&" +Xep*33Sma._co3tl3ATc<5@]`WO`79+4>'WMGLV]J^WYU05De,?/Dd;jfm68m.)H@[JfqE<@4tVI]Tt_ +n%8=`niF>)kB^(k&DQ:Oe%3mYT6?g.?#D'6\ZkPnan$e8jTcsB-[,=]Wbt8a/t[7Jq`\?YdGA0C:g)kf +dN@YaI$rda?+s.I/9X$cBuM\@]"<>E+Q`$+,cZf)>\`WrK9XR$ICs*_`%GDS"nL5RZAuQPf'9N`1&Ch# +jbp"Wh;uMf26h9[\?gU9:iNBkq.Eu#laeI14[25XF +ZtF.n/-@kB/U)1)?"a6W7S#B%P45rF]=7irDpBM[(Fha8A2JU0DsK)?Z#[;*J(#k(I68$sqrcX8(=.I> +)+st>G)?/@$lAf[=8^CLZi2E0q`R$AV"Bu$V)?/a]9H:YOLZ2C4rlFO)`dB:dMZ6uY$[&1/J8/pOD0Dq +OqLZ/:ggKr`[X>Cfs?DFI)"3(Xf[/[ps$X\W+F\hb[Rd6;iGaogeW8%b.Kc-1!di2k0@#>rGCgM>oBKM +F"9gK_F\ZcjImr1*dUq8N.jk!c0f(ER^r@IVIRkU%1K%;C/uEB`4?;4Arkf8gdukJ8:Qq\\-NCImh+4` +NFcl/R"_&CepCOMV^t9"QT*dAEu#n)%qXOA@sk&FG9;n)O#oZF5LW-$)A;$kI,B#S1j-4`YVfoc<:1g& +c`1%S23!.-TjiGL58p.#S7^6;&o&^$fP9Wf9TGk!L=L(Ng`@.m6s7WYNKhJ%34L=io5i#:&Q+"eRdnEb +J&CB>Kf,EmXgL._/38a$UCn()akq:)T(ZdVO8/!h=?,Y9\ugq42XY;X&+40,_*&[$hQGF3W.?o\3_Te/ +,>K&X;9;@W3D3"r4@`AV7@:A]VPD)(5'iOJN^qnS5OFdC[R9DghTJSb4^00Dko4#kqOPY2/aG.7*J\^` +3o3gh7)']gC&P_DFD"rLl7%&_Sg202S+`pu!0sEoQ$\Nb4hTAt!aE?H\f?SJYt+'(i,aA)IE"VVrD]@R +kb(?n6EkiW-_-mb.q8;*QbGlBFs>bkB#`1Z=#-u6$@`'A_',OZkcm1cR3bf,bB\I,m&rpfn+#%!R@<-k +28$ZlJP2.ZSTT&iq4:sI),'\=(4d1G1U$#i5$rCO7&T4'?OH8Vqh]U(&(a:uqfL!XfEE$h*kS)L-DRc< +7:B9fB,%a_>IN13iPI>O+$][;UiIttH\A#DdEq.'-#g'6E4r.WI!p?l=9&JX\bkFA20_!4f[sa#o +/YOR$^D]:.p?CNpj%P]lRcPjJX]t-QH#tQo[PrOWXf]*I@Mnlt1KAf`>ZhgYc[-kD/+DBnfJA54$f5'S +h2!I%bdaE3PmQJqB2kbG3^[?lPSrLB1Jbbu?g"t%"f"$B'7%nn4*(cXVb:$KGudqO12M>sRlE3ueo#r* +rBSH*\&`tqOLM6a(b==n="9.#Ldi^Nk,OtU"*\..h*T^f6f0eY!B +]=l@'JO_6`'!kYfXff@[d6Htt4J*_hWaW4p3WA7Y.JgmZ +B(d_2[F8t]@=nC"kBH^_VO3.#'uH;][j%Hr/%up'A!2[a>`C+Oc51F+@X`8*`M>_%7h:T[0q7cL>f'S& +C=Mu`j3jE&Va@)^LV:i:eD&,Z(tZ:Is/U,Gs&88aO!!3tC-\<$)8=./`NUGrIDPIO5N,H.:[Hj)Sd6*$ +),cIV\T_IYSDfNe]C*3?N-\epJZ-(5SfVP[,/q;3jukuO_>+BEOt,u6f6\4.K*gL>"s-8""J,%KNQC!M +^JA"tZXOT+dKgPT*sa1Tm9/Ecci,]6*TQH]9XPUkf.f'i*Uan=m-N!oOdS[pRm`0Pi59ueS`;6LOj1kZ +Xc23e>"Ym4dT="4=K.`.`fS.H.:U_&f82]NTeho0GX"(GTnQn^kh6O/4FG1C[.n%i3KtKEVu\If(@YR/ +`W-j]Gj0KXEBaYi7@qsnj"[O-\s&@oS2Jirq.O/mJ\!U2!],pi^XRRjTnrE:FBV,?&#L4V*n[,UQHI=: +FbWm5;H,gVDlqOTq+L@rk(/Y?mkHJa8"M+('?l%*jP/<[.d_V-oAATOp#U$a@>n6`4*jT4Tl0MhR'5Ef +CHZJ;325[g:mniRXn5:$jblNV;5$FcdHKV!H?$@arbRg!m509jQO>1VclHT(>#qhS/mE@Rk$0\=QP>sG +MUk2ik#S&]EQ9PmJ5e>hjDm*6k]mK9=Da;80,t(;4t_-3BQ`\Qo(GK+=SkSDf;Z#dc%XUrEq%e"KtQ"j +&W:Pu^`_Y2R$YaQ4@LRS]3hi*n`)X9\@@;M>C;tQ(X+sdr;Gh^Hhgjnr`3gjDf.,7MhWu3"#K#!CS:>e +A6>TS^-iBR;c8sk(=COjm^t&WbS-'/rCC,l<2@-TNVK*RoP:4UWFpi!VPr>Z/sGaLO+@ +hq@97[9b[AZIptU)$!Z6$;PdB/)=Zn)Tif:PZM`*HUN%-Z#I^>/2*\Wrj\==9b0q[a39FQ50^TdX<'\[ +DQ@c*3nH?7\Q[FcnIUmu>BELR04'#^*@OCN-nYCnh^o_Eet0fS)&U4!#'2`eX?O_AR(p6e8D/% +db\f`ia^OLY1Br?$]Pjn*_>fG]g#^kIA0kmSb;>)l)9u8*#+YQLD-0rIFq>%K=mB>Ce`qfk*'\RP,GhU +fLF!$1f^oo<>pI0)\>;7*&Gb`M]WD2k>GMW[b=IPc;78+&L.60jLG^9&*:Ej-F*3EBf%m(CK#YCL=9h" +E(m!ZdN#sXr`X+(;fL3PZ#_gLQW2h]R6($4R*15Vq-HJuPLb2/9N.G+a+:78&$dT9Wr?g//r::KeFP>t +lQ-!&=%rA_#Q)V+Rk=NXEBHJ8o<1-ff::$u@b@N9)2S3s%jll=c$=dCY+O_bA +2s!q3nUU=F;-V-fZ?H&lHPPeu\+W_Y%N%d`&m=kS^9(O#X=0"D\4&7a@MJ2UbM?Qb.(.*8;>8k*id90U +@(hsA4)c"V_]J6CiNmtKA%\8*aCC'-<)hC#[6,Y,A27+]?g('![X)LFrj\?`(5lXCaam-[rfAX9G+?%1 +:Mg4+[/\?U&Y;V3LUH7<0XL=$PjE\\TR(J,+XWcC3R:N:!3=(**^]W%AQ +>T!]DZ6MfSb]I&nm&!0]>-p^Y2:oVqp;ZI72P*J8O,aIO(_O/#RR$<"CuW1HZE7`&e7J_<`mnbQL15C4 +GL3Zb]>&\&6da1obRL"DYN1%d*+W\p?=eP[)Y*N5>_l'NX(r8f7lf&]2f;2T,]M%VOW-;tMf^Wkp9S:+OanGM]M%B0D.iLKS08 +'kT:-AtOoq5h5M-@aB5bYc;?F8YiG5dHk'Y92gp5]=Vl5mG6US +n"dXt\tcu`pXIL>mbCaeqVFY`e#W61m=#u?h7]Xtf\XT[jO!c`9;2(`ZFrp/eGJsd4?[YqHZr9KC=o)( +KuVHP]=2l^CYb0=gq/WTo"DZUA$9Y?nl,!uXDnfLr)*2^3=W;-nHj6`IffBEVg1i7MN4Z_;SCU`R"1e]6[9b\276Q^Pna +8j=#=c=90E@bKNd5 +T:'NGUSYXl/(OOlV4h!3_tI+-V=j'*?]JO#e-2gb>2=2V3d"JZ.c@X\a#\J?@+Fe[fe\;sD43#iJt!L> +n+ppjPL7M]'HdNHmB3kCZ4pI%4)'+C!?@HQiOFAB<-8Ba=NTEh\J5rG.sQ8tDog?1:)Yc6Rg&UtI5aen +`qj.$X5DL&r_B=&pcR2&!r(gJW9WU?7$kq0^TZ@2XA`H4HaPIFd::M3M>)6FCGt]Wm@pU#k.iJH^+!\K +O%@[PJ+l!A_KGLGPY(K'5!3-=,T +B_!J7[bBJ9S8L_Hn_nj,IHXi:W1.Mdfjlsr'1HE4VWNnY>%#?P:f5Lj9m-'/@i?&]*mU[=Rftls422H!5`rT-hq"[\4_0MV^(9+r)!g,J +akV$e:f`%>hX"C_AcPh&N?pH)MM#*V1Arn3heE$:[_R_Nb,IGYb"bIOA%B58GACff:t*LljI5L(c,HZ- +aZN*;s4O<*oUq>4DJR^&bc'q.9@9g"^55jLh(XI$6^JNqjF!,(Dd#\(CH[Y$UqYT)XZQHj9G=EKaIOAHSZr`cD9; +9qjA"2u`rKp&htH;fhO[c;(&jMoRpXf(,VD\1"$IJ&.BG1EgVMK-?*(V$L1 +:LL_q/#u$VNn(0UXXeZr\]LG>&In+1YF_=+.>iBRk03&&=gbn:@nRkC?Lu?48EAZnE'1eb>M@]%P6iLW +.C2e$@3r>I&-oUDZ_LXB$SeSYoIhXq]D*LYj,,=4P"4quR]hTWKphG!>j_J`[)>=$6oCIi;qT"[=SjPj +1FQb4s44d3?WOLkH#RY33:n5r]PmQ]1X!*/c"tfIH#NSCVJ2:7KB(#(L1I*f%bDM$^5-UY7VqX-.r)YM +>ZV'<+cX#)gU^\*I?']ZGOJR6*7m$VXXG7A(!$7"Ab\R8?LralMsLV!*i@O+6H&8Q^0n'*"g0=@)JH)3 +A)JsM."8crK8^Zhc5i+KQ07R+&]AhLegg&R%UAX5F!J82Ad`]\h)6/!<8$&gI'[3@QMFKQo6k'W!A"X'd:27h)%)H@XMktF]DNk +J&Qj.edlfe:#2b9G/mPiN/og$jh#"*pUmV.Hqc6^(]"c``QDCi\mb"M;1`L>99morMHuqcneXk^>MZ-(!\W)n7aH<%Nd`t9lDAl3LNZ5_MXYhkWn#ss4)=KEXAS5n +]h2=^]6;qec?jn85HH_!"A#\e]bduAHBJFXMlA'q,$@U"gg[i3qN,0aggWT\Q-m;1^Lt5]FPk9j-L.IT +]>\mg5PAr2iYJheHBVCr(/m/_9GURq^37j+*P',lr3VQ3G'uGke"2?6e>_H;:"64c"1OYDEuV*X=!Ms( +XOX_G3mZI""RmSf4rYE6$6?G]i[4Y\R0c3PU/(Kr_^: +^Db4D.t0@=G$21Ij'J-eR;Gdffk%d\\cf4UefJXRZc6[nXi1la?pFQ&.TG9k1V3Vb,>iA#/)XPC=ZRa[ne'V%+F0i;;3cf9k!+_p]Q6P.nAE5-?.ab9KBCJ`5J\NMH,d. +T<^:in=rkR/C1RT[PT#]$'+3[qsE'lr-- +Mj;p\>S:[CeP4n(KgMK]G.Ii>]-#p=HSgjXqm/ED];Cu'H +-\8'3k3q7ile*VRanj9kHY2k2%o07,[L`5/C)sE^ZILAp8[n.Xajjo+cXn0G7/S0=Dc)O4"WAu"_ou+_ +`r/:q9l8-VgUn!NJOR2YX!`I%Z.1`@IQbolB"Tt"MTrOM#=>" +gTr\Am`s_DIS+uK`C\JCgAJL*S7M8tX"4PFHa1(:iK(d^$2a=$Ys88dT&BV4IJ`sV>[>KP)"dM>=S9s^ +_OC0KhVBp3B87&E!OH$;?1o6,<"9h0AXD0hl*Zc)U_QLo3+/(]G*QIsl)(`Nd^`F0tO\=pV$'M](^?^seWqd3A6k++7#N4.hFZ +:P6"5qKYi:?g7h'^=Ptopj9>dd0`Rl)bRiJLjWY/-WeK]RtK`QEaPPGj]q5f*-udS['XgCS9.*I"DnuF +,jkH-gQ*qtknPlI$A"OQa\dlJ,I(2j`ZqC;FM?PZZdm3KCn3I[dE8&0(9M7@k&'UsQGKs#B$lY;[M25T +2SF%pn;Kt$5IPCOU1]0;rXV*Tjnek5(lfKdflJ6AnFnji46c8>hd?A;YLrD\J)aZXo/NfAIPWWQGKeN< +?.mnQ2Tn`'\OGA#ht7UF:HPTtU>LAr2L7'Ij4([(JDiSuZ,ZC=fQs-7"h\NT4E3J*;-72qm]XsOi0'@t +'UZU&bS#.t.=Ql^[Nh>dVfl:"kZjs/p@-74LYn?PqHZJf<`IIhP!%?erTDnuln-W,FB;+tU&I.Vj^Y\4 +kB^3fWN<@.95A7BALLSPps+m?3./b)]CGeLNn)iYGIC]PKsEG94ar7THh%"A^8kht0lrp5bb$V!NG!bd_i_[F +qe`I,IHn5p/9>=TGV%$-G@[Sl@YatC=e<\')n"kA;M&C\[rpmqAc(S.$C@$A7c]4Z^L,adkT4F7ZV.pP +WlGuXW_8KW]%E##9IhjiY5>[!ZaCaM=[[_$Z/8JDD85s2j\AZQBC4eYOH+$-IAmjrD,'+ik*']Cb[:5t +AYWeqADYqf`ET9.IH\-:jd\=ktbppNc@CLE[87:fI#,*W*IfY<93ta2,HNjt_F;9p5V7b;C0/a-WGc&:T)_H7!qXD1mSQ%4Aj+WB%a0)HZ\K!<)13#RsC8g@0 +fkN/']eSOi`A0TCI]F)c%W;3Ib%m?PbBCRgRqjMqHhr0IafU,ob%j@-ZCA4VqO@BmCW"E_dIm0iVt`&3 +?_kRU(,9>7[,gH,mfJR$m@9:dWjYqso_G!Etf(`uc+U^<9QKhUEp>2Drmo=3ZZSU&86ojmtR6 +(fd(AZkI2q78[[)+I94`GuAK32f"qiqoVkG<:P+3Ed`9"E,LtaY&9\seImckY,,1F:>/LN]LLIum[cIE +K71SPP8oK.Ti[@F%b.5=O.D!&i(6qO.-!@rqqTj!]%9YbCNJp'RJ1uJSn.W$KB7Ws/)e!?YFAZa.Y-"3 +8bYA`cKj-1NAUj62>MVbfXBirb]X9X!qJF1=m4<*0Fa&*@]CI3XgirLq=m)$:!+NKnrN8UF(+G7-B;\R +hhkK$d`a-?4^+niI8L\D%\<&Ojolo&e/7-s>i5)GRE[:Bn`b20\>D<`hkhhN&H!&4bKfM*u9FQjfbf-1ORq-EVhe6 +5E?=oc*?bc\HbP-Z?m3)#H)8$3ai%G9p_EgFkmr]jg/kI<[A*pa)pT`'3+Y@t!\NSP_kWATN`9p?fN5:0s+Hm=J^1 +4rWUN(PuK;%fY+iq\!4Sn4UeGS:5P*e7\AY(%8KNS#"W,K-U*OF(=7B?EhFRfY,9nqAJner6k(E-&*mm +Pcd*pMXGV@C1Sc8@X!O:"?c4=&n:%30O0d;J^WCpB;En7do!4QBOaa'E2Fc:]?i<*2m:6;9mGn"F,ESuemgO,RL<<. +enS6[U0(ac94rs`o$Xd5BF_,e'_dr7h#fV6_Q$'>/gobtr5q%l5.TE?[PbF6k +m`7,$KI4B(erpV$kN7`DZd[97m7q)uep"413;7a46!uS+*Q(.>cQ=`Mh4@db[Jb$8nJ=ucAN-tLMsF^J +dJaaFakH95Y8-i%1WV"aYM"RXh$[@>+)#" +2g$af4OG1og\Z!oG#$4flF+i)N4aS9-093*s8L,%,q(d5rfUVXi:HfQrJS4jP%`ZV1[] +eum0Y'GNgBiA2e!*b/Mq4I-g/;=3NA`7l"/'ud3cGP7Y+S*1N'8U+_"*hIKG?ra,NoB\ +c+I&U?E%^pD6:B_q_rq;ZR;!OGTJ`pKg.388j6^$@#f63+1sBQ.6\=\2=eGGl9bF>q&Om/C%d9*D&56($"Z*C`9[;oCHBn\mZP,u> +4Bhtlq\B1%MkbJ<]4'-BoQSMT+^I1,6h/AiB.\5nOu*)Tqb;W*MM!a-hQEkld9=3uBhZ3sdr\#FEUYL-\OA`IcO3!-ea/DW&Qbf&)-BPo60 +6Z]LQ^%4<;hc>mtp&u'k&]73t=1(3o_`-GcceOSPZU)3B_l^[6qD94l:P\S:Q!A,YhF?Ph:fZ&P`oC`2 +=%/jHn*[ekG']5#.ZE;aY2aY3!CN_/"_K24IRRKXTq.`U/8(52Y]Vm2PjV]b"p\oR%G;`pgRb(raHZ]Ii=GiejlG@Sk*E2d1rajKtr +oV6QLi-'8s#AERfNGc@1MXim*4EQVgFDb@OaZDa;ipR>mB1jgZ(ks]fJYPhKh?R3rgi_I[E$J?)oOHkH +O1!c()Mj;28'+#1BCrYRA\ujl&*+>QBLUa(//&Qu\fF(8]5\5p+@b2hf\?JYZ=/*gs1e<0K& +&q;lqViI)[Pr;b=^@P7NduEVuX4cVPNhJ=qk.cnT^FPR(dB#ckCp:#lU]1tL@'9+P/&1lpC2lG +09.N5@_@8Sc-RH:Q7/%Y'gtA2cj&^dXN\G7atc6_$!0]:?L%b!N%7pe5DYPbXj]D@U=p/p-X=lH3Ei`n +_Sq.NY8K&edD99Zpl+Y(5RiXrb;X^AMdiO.T6W?smR0G8@-n\VS98(Hm#:pY<'PV/$tfZ3j`8Smk?T+7 +4t(?J6,T:And)D`KS'dn-QKfcJA`^U1n +U@i?@ookuL8ods\&&_+Lc?DI2#$'iHfRk/qi.+!)khX9s=F$HU=nbOE2B9VOWfrRSV@bG*D@e_M4)];; +(s,-(grS4TF(g]*Ll6F_`rf[$XmX))4)^UC)p(s[9AULpUt`[RG=FABr\BnPESdoS_Q@W+aB[*Mb_q&Q +g][qAbk'gf)M1JMgY5qPBZjV31JbjD.dth;>"unjA"5T,0fDA\M`4Ae9ocX$!@)Nl-=WTN]<)nZDYVs$ +LVu`Z.qAbA\s$UaoHM*pL")/O!$E]/SG%0,8A'_&\!k0:3;hnACtc +4O1g5EYQjW[Eb_=-#`=!ZgP^[>j%fMVUd$P?5-,<:[$$!&]9sqr0JLTrVPF[jJMTt]buPHhMc]r]bR]: +Qo?7=@I;V8_j_8,0Q0BBA)0m"jo6]A"W[e'BB-?Lhd-?L^: +WpGlYGeaZ>)fL6L^4]Gh-+DXO)kc+0KW;>WLCI@GMP+HY4hY+YDnI_E +=X3!G6HH&;`tjC_D=UD6S"Q?*N2KC*!V1=`4tcI+5:H#TI5hW;GtKbH2f"s&;D]Wd\FWbT0)-_t7@P$a +;8ZJ?aIA3*pi(Itk +qVtk:#;P71Z)G#U%"AC4]V1;Eh]*l]6pHg-GH\=dXUH\8?Tu6"0=.n3OLY+.bdu5]5i4\Eq24S8%uZ<- +>:\Q<3.>9J7LuT49nhuC;Oi5I`BG=A$ +D$\gBj_)ML+j4UNDk0qJeg3KSG;.f_N +^$P65&$NEo)`+1^,p,:K[J)q*-@/(Jk^GJ8<$=usVJ&7l`*!mf@ju^8k>sW&8Yei"j%s;.PkmH]Y5/U4 +\/"Z"UIk3Z*d%URLeY@OcL0oPa)4uk6F_I15oYQ6d5BpEc?B5FnV3kG*Bg$>@r#IO*07V.7-a-]QkY"^ +O)`+qn>5!VkDr,1XBOtN!P?BqG?%?l$&3fGR&KgPM8e3ZH0"`D)2Od=WbU6&EPu+G.WeL=AaYnpql(m5 +4+V\J":c9XQ#2gLOTZo(>Oq_loE$CP=FPFSbmoh4P%(@i1CJECB +:/lt&FNM[ogo.s;WCp$'h(pJ8YRXqCNfpah_)g=]iPIRFlF`t\W)pW4d,%DEKqhK#hRa#WUVd7Hna[S[ ++llF#5"cmU60M;9h_:X7m7t\]mX\N,ML#FP'/`-gV'gTNOJpOnY'O37a#'XSZ"O6+/1YVc6AAQV9ZMtI +S^a;TB49?U-Hs_j,KPpSpDTBj[R-o5"HTi3\*//b/UMq5`S4jE8lArZ5PYmKG17PP0fl]5uZ@I_u'i!gj5 +Ag@h%Z_K!ocD9koj:.iOds\us38K/?$"4NsL,`YhL:7rmHf-f,?hqUk +QCYa.?Br_bKd`'\Hb.2@UtE08T>VIlicIWtZ;SAYn-B9S$pM_jrg;-7=o,_/F>M(uYZ1$/3%?OQaXgV) +A4PThrp4LmjuJ@6ZGlXJqTsKXFFH"/5X)guapK]]9a/hh43!"`)ud?,9kDL)H?h>.\9AKaD"SO!;2!%HE4I,'.=tYVf_%P8FC7>tG>m^MlVSIAk:_r]$T#Nf^#V(&Y=7=e8=io2 +-J]Tt*SFQj/2/KPo3B;>\/MM4DN[GJLd#UJo35c#Vai8.=XX?cJAs-DkFuVJg]CLFL*+'Db;4p[YtV"J +-jY6'&,O0,tJCX6o'F'9RF40Dc%.c6FE@(qg]If5'P+tCR5F(*l@,\8q`S2H;BBB%h<^^G+o>[E.8eoC^">>Ah)%^`P_`=PQ=C%!PDA[mL_WVK4XEE(P?IjKM"R:=T+W$!;^0e[S<$UB +ou;'G#H"grVaS0k3r:p66R#sNVaRJB1]jFi;K%"VjD4EJ,sou$\>M`r;HWB:O^U?;@/tMA'&dRqRQ>Ua +KMl_FG18"DaA7Y!^@V?u4g'uK6Z;I%42Meh/n0pm[A\Tqk +;ONSD^aGp:@RC=BW)FaDDeCq6PW(!GMpE!]Lg_[4T7fNd"q?h1R"h.6#%?imtOF +/qn:]E.g$B*2>lCmTKr`e6Qj*.TfpJ&_=-tW!?D&bU4n>/9,Mm.HhE-\<"$Vg_dIWSX%%KM0J +kXFZ#4QI+Q-jZ.rXK+]A1)_j:Q*/J]kA2:]b3bqX;2X1+D5@8X.!TssF2,Fnk$6H^9#G\QYk$ME71M(: +Y:M=jlZL(%124F%\#ZO]4O6@$gZ>s\RjfhnD\[B)8*:Q5^C]$1`E8.R1Xk>6CKlKB.eoH4jn=hVXf0]=eW/O?h@AG,!';Ld&g5%[ui050CX9tr%0DQK&bY[c$95GdJ-884qN/F\]Ep2X&=]nbT_G2*_)CY;fa?L9C@l +hlp0.FKB6`m0;k#'#LjNIXc*M_&iGt"bY&6og=(RqUjp#YDDgZUq>uq,NZ2@%`kuo-JpCo778SPcu<^#$d1cO@+ +?#toEq:r.ac&I'_?d4SB[(>CSk@qOZ4\HKX>I:CKB2V@51cHPJAkR3Gb$OSL%s1[k".ARk=;]V\8RTm/aVOXFin"luaD3t.A-u<57%=!!H +gdmpV2)dmr\Tt542u[j-k/R.uX1>K2a`TJ6og#)dl*ssJ(rsO:W2A*T7J\Q!5-EJGgMJM\?(bGMSO7HC +uq,3 +je?X=mDWqSk#N4M@@Gq<2T]0t9KLYXJ4ak,c-Z@TYI3(J[9E^(@t/00SEKOkgpV!%XuX2TWfg+YF1^hf +gb'Z5NUIm%DYCcjg`AH35OoIlqeodei:beX8,,A4,fa%:^PECK^UWOZO&W5jPcfMRf46NkQF3NA[;,Ic +p5,PT2Zm?%[H"Sb):bcpP#$5TGJNE$4Z'6#TK1c,7kb"XX$4G2Xi7+Q[63:Y`38B-_lStC/[T9d)4.@M +[nRe,ijUG=)2)3?.>4]&](BeTG=eh*h_^*_cXu^i[;?D_j<4?#]>7D8 +.fWG9;N8:39F`u0aI@?^f8U<<]bT(cQpDDN;d3$90cX0c57udu4f3tCeR+=2cKcX]17$lBof'C9[(Nh3 +i]D&n=F4X-khGXd#q-:QrSb-&5Fe].sph7R7OTJPDWH`9&#fA$\33_3-G2VYT'%X;4KXl.qtQh +9LLk0*0r)Y]_*:uekG33U.72QG-p?/,0h*]DCeMPV^FsPd7gPgRir(-:XN_,I$5B21TK+<8-uLfet4=pg"0qkh)/mIj8&9lVaX(TOJ`4V652WF\=S*?IqH5?)T=U7%`jYLD\X?So`e"#&Ke +b*(9ta_eMQp7F7&\k@-ZR[NugWi@s\Nib=i%Au$#\C/SO=d27-5J3Sm@D!PC0Ni7R\%R[oF10I@dVir% +414GjXhIGohW[^[_ +JNJZH@U>Q=0s0.("l=\Qk&fIE(&?'g@>5Y7fu1E8l+O-=0#`!Pc"C+BF=/`GNOr=$NB7ihS\@ifET,&J +p=:OmQcU@.l`G.+.fO+<8$]?:lb)rDG_X5PcPl4tdh6G,'L%428&+CnRggp@`&/+dK33I58F]TI!Y+d0 +GDcsuH[:QP\[E(r+Y_1r]]uW)@u%'XDdFgoC;JD+<1D45epK$`PEm`%b.H``ea(^_p%FrjH-7$9,FT=C +.\s>8H/i0Tm>h\"):CX4FB]V-:6)'.Q#8n3:isSWrbQPg#_%Q6E6YG[QC(p)WtaY9*5J+7d7Qb6,B$oms`Y2B4l:\)!O$YWAa, +*uNI6>Wr.ijmms]eNW$rn6.FdGe7b4LHm1.!p`6+Bn&c>!2YHu?mXFdo"cO9D6uaN3?IaB2,_J[&'Pi7 +a9Tm80[*dSO:V+aI`MapS?4Cc@e;]hE&@Oe>/MiEj1jr0.rKgWLEZu)aTlDfKR@1I`OPJiX4:H=0NTC/ +"L-%S+Wl*u=$Ej_BR.sZiLkm0?F5j1dr*76$O1K1X'7`+'n&cp_HXk7csBiFH9C_GrtAaVkDQb;=2/q< +okK7hrpfsY4T=gLcufqMjBJg;#i'0YjjP]_qtc3;0oYcis7gN1a++'XrrY%mr_E~> +endstream +endobj +7 0 obj + 28739 +endobj +3 0 obj + << + /Parent null + /Type /Pages + /MediaBox [0.0000 0.0000 431.00 434.00] + /Resources 8 0 R + /Kids [5 0 R] + /Count 1 + >> +endobj +9 0 obj + [/PDF /Text /ImageC] +endobj +10 0 obj + << + /S /Transparency + /CS /DeviceRGB + /I true + /K false + >> +endobj +11 0 obj + << + /Alpha1 + << + /ca 1.0000 + /CA 1.0000 + /BM /Normal + /AIS false + >> + >> +endobj +8 0 obj + << + /ProcSet 9 0 R + /ExtGState 11 0 R + >> +endobj +xref +0 12 +0000000000 65535 f +0000000015 00000 n +0000000315 00000 n +0000029482 00000 n +0000000445 00000 n +0000000521 00000 n +0000000609 00000 n +0000029458 00000 n +0000029936 00000 n +0000029652 00000 n +0000029691 00000 n +0000029793 00000 n +trailer +<< + /Size 12 + /Root 2 0 R + /Info 1 0 R +>> +startxref +30009 +%%EOF diff -Nru ausweisapp2-2.3.1/docs/installation_de/README.de.rst ausweisapp2-2.4.0/docs/installation_de/README.de.rst --- ausweisapp2-2.3.1/docs/installation_de/README.de.rst 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_de/README.de.rst 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,489 @@ +Installation +~~~~~~~~~~~~ + +Windows +------- + +Der Installer der |AppName| kann über die Kommandozeile gestartet werden, um +den Installationsprozess zu konfigurieren und systemweite Standardeinstellungen +vorzugeben. +Der Rückgabewert von msiexec informiert über das Ergebnis der Installation [#msiexecreturnvalues]_. +Neben den üblichen Parametern [#standardarguments]_ enthält das folgende Kommando +alle unterstützten Parameter, die im Anschluss erläutert werden. + +.. code-block:: winbatch + + msiexec /i AusweisApp-X.YY.Z.msi /quiet INSTALLDIR="C:\AusweisApp" SYSTEMSETTINGS=false DESKTOPSHORTCUT=false PROXYSERVICE=false AUTOSTART=false TRAYICON=true AUTOHIDE=false REMINDTOCLOSE=false ASSISTANT=false TRANSPORTPINREMINDER=false CUSTOMPROXYTYPE="HTTP" CUSTOMPROXYHOST="proxy.example.org" CUSTOMPROXYPORT=1337 UPDATECHECK=false SHUFFLESCREENKEYBOARD=true SECURESCREENKEYBOARD=true ENABLECANALLOWED=true SKIPRIGHTSONCANALLOWED=true LAUNCH=true + +INSTALLDIR + Gibt das Installationsverzeichnis an. Ohne Angabe wird der Ordner + "C:\\Programme\\AusweisApp" genutzt. + +SYSTEMSETTINGS + Betrifft die Erstellung von Firewall-Regeln der Windows Firewall. Ohne Angabe + des Parameters werden die Firewall-Regeln erstellt (true). Durch Angabe von + SYSTEMSETTINGS=false werden keine Firewall-Regeln erstellt. + +DESKTOPSHORTCUT + Durch Angabe von DESKTOPSHORTCUT=false kann die Erstellung einer + Desktop-Verknüpfung vermieden werden. Ohne Angabe des Parameters wird eine + Desktop-Verknüpfung für alle Benutzer erstellt (true). + +PROXYSERVICE + Um den parallelen Betrieb mehrer Instanzen der |AppName| zu ermöglichen, ist + der Proxy-Dienst notwendig. Der Proxy-Dienst übernimmt die Überwachung von Port + 24727 (definiert in BSI TR-03124-1) und leitet Anfragen an die lokalen Instanzen + der |AppName| weiter. Eine Weiterleitung der Discovery-Nachrichten (Ergänzung + zu BSI TR-03112-6 - IFD Service - Kapitel 3) erfolgt nicht, so dass SaK-Geräte + in diesem Betriebsmodus nicht erkannt bzw. genutzt werden können. Ohne Angabe des + Parameters wird der Proxy-Dienst automatisch eingerichtet, wenn Terminaldienste + installiert sind und das System im Anwendungsservermodus ausgeführt wird. + +AUTOSTART + Durch Angabe von AUTOSTART=true wird ein Autostart-Eintrag für alle Benutzer + erstellt. + Die Deaktivierung des Autostarts ist den Benutzern in der |AppName| + dadurch nicht möglich. Ohne Angabe wird der Autostart-Eintrag nicht erstellt + (false). In diesem Fall ist es jedoch jedem Benutzer möglich, die + Autostart-Funktion innerhalb der |AppName| für sich zu aktivieren. + +TRAYICON + Aktiviert das Trayicon damit die |AppName| dauerhaft im Hintergrund aktiv ist und jederzeit für + eine Authentisierung zur Verfügung steht. + +AUTOHIDE + Betrifft die automatische Minimierung nach Abschluss einer erfolgreichen + Authentisierung. Ohne Angabe ist diese aktiviert (true). Durch AUTOHIDE=false + wird diese deaktiviert. Der Benutzer kann diese Einstellung anpassen. + +REMINDTOCLOSE + Wenn der Benutzer die |AppName| per Klick auf das X schließt, wird er darauf + hingewiesen, dass nur die Benutzeroberfläche geschlossen wird und die |AppName| + weiterhin im Infobereich zur Verfügung steht (falls das Trayicon aktiviert ist) + bzw. dass die |AppName| geschlossen wird und erneut geöffnet + werden muss um sich gegenüber Diensteanbietern auszuweisen. Zu diesem Zeitpunkt + ist es möglich, den Hinweis zukünftig zu unterdrücken. Durch REMINDTOCLOSE=false + kann dieser Hinweis von vornherein deaktiviert werden. Ohne Angabe ist er + aktiviert (true). + +ASSISTANT + Startet der Benutzer die |AppName| zum ersten Mal, wird die Benutzeroberfläche + geöffnet und ein Einrichtungsassistent angezeigt. Bei jedem weiteren Start wird + die |AppName| im Hintergrund gestartet und der Einrichtungsassistent erscheint + nicht. Durch ASSISTANT=false wird die |AppName| auch beim ersten Start im + Hintergrund ohne Einrichtungsassistenten gestartet. Ohne Angabe ist der + Einrichtungsassistent aktiviert (true). + +TRANSPORTPINREMINDER + Zu Beginn einer Selbstauskunft oder Authentisierung wird der Benutzer einmalig + danach gefragt, ob er die Transport-PIN schon geändert hat. Durch + TRANSPORTPINREMINDER=false kann diese Abfrage deaktiviert werden. Ohne Angabe + ist die Abfrage aktiviert (true). + +CUSTOMPROXYTYPE + Teil der Konfiguration eines Proxys. Gültige Typen sind SOCKS5 und HTTP. + Um einen Proxy zu nutzen müssen alle Parameter gesetzt sein (siehe + CUSTOMPROXYHOST und CUSTOMPROXYPORT). Der Proxy kann nach der Installation + über eine Checkbox in den Einstellungen deaktiviert werden. + +CUSTOMPROXYHOST + Teil der Konfiguration eines Proxys. Angabe des Hosts, unter dem der Proxy zu + erreichen ist. Um einen Proxy zu nutzen müssen alle Parameter gesetzt sein + (siehe CUSTOMPROXYTYPE und CUSTOMPROXYPORT). Der Proxy kann nach der + Installation über eine Checkbox in den Einstellungen deaktiviert werden. + +CUSTOMPROXYPORT + Teil der Konfiguration eines Proxys. Angabe des Proxyports. Nur Werte von + 1 bis 65536 sind gültig. Um einen Proxy zu nutzen müssen alle Parameter + gesetzt sein (siehe CUSTOMPROXYTYPE und CUSTOMPROXYHOST). Der Proxy kann nach + der Installation über eine Checkbox in den Einstellungen deaktiviert werden. + +UPDATECHECK + Wird die Benutzeroberfläche der |AppName| geöffnet, wird eine Überprüfung auf + eine neue Version der |AppName| gestartet, falls seit der letzten Überprüfung + mindestens 24 Stunden vergangen sind. Liegt eine neue Version vor, wird der + Benutzer darüber in einem Dialog informiert. Durch Setzen von UPDATECHECK auf + false oder true kann diese Überprüfung deaktiviert bzw. aktiviert werden. + Die Einstellung kann dann durch den Benutzer in der |AppName| nicht geändert + werden. Ohne Angabe ist die Überprüfung aktiviert, der Benutzer kann die + Einstellung jedoch ändern. Der UPDATECHECK Parameter beeinflusst weder die + Aktualisierung der Anbieterliste noch die Aktualisierung der + Kartenleserinformationen. + +SHUFFLESCREENKEYBOARD + Ist die Bildschirmtastatur aktiviert, können die Zifferntasten zufällig angeordnet werden. + Durch Setzen von SHUFFLESCREENKEYBOARD auf false oder true kann die zufällige Anordnung + deaktiviert bzw. aktiviert werden. Der Benutzer kann diese Einstellung anpassen. + +SECURESCREENKEYBOARD + Ist die Bildschirmtastatur aktiviert, kann die Animation der Zifferntasten deaktiviert + werden. Durch Setzen von SECURESCREENKEYBOARD auf false oder true kann die Animation + aktiviert bzw. deaktiviert werden. Der Benutzer kann diese Einstellung anpassen. + +ENABLECANALLOWED + Aktiviert die Unterstützung für den CAN-Allowed-Modus (Vor-Ort-Auslesen). Wenn ein entsprechendes + Berechtigungszertifikat vorliegt, muss zum Auslesen die CAN anstelle der PIN eingegeben werden. + +SKIPRIGHTSONCANALLOWED + Überspringt die Anzeige des Berechtigungszertifikat im CAN-Allowed-Modus und wechselt direkt zur + CAN-Eingabe. + +LAUNCH + Startet die |AppName| nach dem Ende der Installation. + +Alternativ kann mit Orca [#orca]_ eine MST-Datei erzeugt werden, die die oben +genannten Parameter definiert. Die Parameter sind in den Tabellen "Directory" +und "Property" verfügbar. Übergeben lässt sich die MST-Datei mit dem folgenden +Kommando: + +.. code-block:: winbatch + + msiexec /i AusweisApp-X.YY.Z.msi /quiet TRANSFORMS=file.mst + +Um den Start der |AppName| auf Systemen mit fehlender Grafikbeschleunigung +zu optimieren, kann die Systemvariable "QT_QUICK_BACKEND" auf den Wert +"software" gesetzt werden. In diesem Fall verzichtet die |AppName| auf den +Versuch die Grafikbeschleunigung zu nutzen und startet direkt mit dem +alternativen Softwarerenderer. + +macOS +----- + +Unter macOS ist keine Installation per Kommandozeile vorgesehen. Jedoch können +einige der oben genannten Einstellung durch eine plist-Datei im Verzeichnis +/Library/Preferences systemweit vorgegeben werden. Diese plist-Datei muss dabei +manuell durch den Administrator des Systems hinterlegt werden und wird von allen +(zukünftigen) Installationen der |AppName| verwendet. Alle nicht genannten +Einstellungen werden auf macOS nicht unterstützt. Der Name der Datei muss +"com.governikus.AusweisApp2.plist" lauten. Der Inhalt wird im folgenden +dargestellt: + +.. code-block:: xml + + + + + + trayIcon + + autoCloseWindow + + remindToClose + + showOnboarding + + transportPinReminder + + customProxyType + HTTP + customProxyHost + proxy.example.org + customProxyPort + 1337 + shuffleScreenKeyboard + + visualPrivacy + + enableCanAllowed + + skipRightsOnCanAllowed + + + + +Für die einzelnen Werte gelten die gleichen Beschreibungen wie für die +Windows-Version wobei die Bennennung der Attribute der folgenden Tabelle zu +entnehmen ist. + +======================== ======================= +macOS Windows +======================== ======================= +trayIcon TRAYICON +autoCloseWindow AUTOHIDE +remindToClose [#dialog]_ REMINDTOCLOSE +showOnboarding ASSISTANT +transportPinReminder TRANSPORTPINREMINDER +customProxyType CUSTOMPROXYTYPE +customProxyPort CUSTOMPROXYPORT +customProxyHost CUSTOMPROXYHOST +shuffleScreenKeyboard SHUFFLESCREENKEYBOARD +visualPrivacy SECURESCREENKEYBOARD +enableCanAllowed ENABLECANALLOWED +skipRightsOnCanAllowed SKIPRIGHTSONCANALLOWED +======================== ======================= + +Nach Änderung der Datei kann es notwending sein, ein erneutes Laden der vom +Betriebssystem gecachten Daten zu erzwingen: :code:`killall -u $USER cfprefsd` + +.. [#msiexecreturnvalues] https://docs.microsoft.com/de-de/windows/desktop/msi/error-codes +.. [#standardarguments] https://docs.microsoft.com/de-de/windows/desktop/msi/standard-installer-command-line-options +.. [#orca] https://docs.microsoft.com/de-de/windows/desktop/Msi/orca-exe +.. [#dialog] Unter macOS wird die |AppName| in die Menüleiste minimiert. + + +Anforderungen an die Einsatzumgebung +------------------------------------ + +Rechte für Installation und Ausführung +'''''''''''''''''''''''''''''''''''''' + +Für die Installation der |AppName| sind Administratorrechte erforderlich. + +Die Ausführung der |AppName| erfordert keine Administratorrechte. + + +Verwendete Netzwerk-Ports +''''''''''''''''''''''''' + +In :numref:`porttable_de` werden alle von der |AppName| genutzten Ports +aufgelistet. +Eine schematische Darstellung der einzelnen Verbindungen, die von der +|AppName| genutzt werden, ist in :numref:`communicationmodel_de` dargestellt. + +Die |AppName| startet einen HTTP-Server, der über Port 24727 erreichbar +ist. +Der Server empfängt nur auf der localhost Netzwerkschnittstelle. +Die Erreichbarkeit dieses lokalen Servers ist für die Onlineausweisfunktion +notwendig, da Anbieter mit einem HTTP-Redirect auf den lokalen Server +umleiten um den Ausweisvorgang in der |AppName| fortzuführen (eID1). +Außerdem wird über den Server die Verwendung der |AppName| von anderen +Anwendungen über eine Websocket-Schnittstelle angeboten (SDK-Funktion, eID-SDK). +Daher müssen eingehende lokale Netzwerkverbindungen auf dem TCP Port 24727 +ermöglicht werden. + +Bei aktiviertem Proxy-Dienst übernimmt der |AppName|-Proxy die Serverfunktionen +der |AppName| auf Port 24727. Die Instanzen der |AppName| erkennen den Proxy +und benutzen in diesem Fall einen zufälligen freien Port auf den der Proxy die +Anfragen weiterleitet. + +Für die Verwendung von der "Smartphone als Kartenleser"-Funktion über WLAN +müssen außerdem Broadcasts auf UDP Port 24727 im lokalen Subnetz empfangen +werden können. +Hierzu muss eventuell die AP Isolation im Router deaktiviert werden. + +.. _communicationmodel_de: +.. figure:: CommunicationModel_de.pdf + + Kommunikationsmodell der |AppName| + +Der Installer der |AppName| bietet die Option, für alle angebotenen +Funktionen der |AppName| die erforderlichen Firewall-Regeln in der +Windows-Firewall zu registrieren. +Erfolgt die Registrierung der Firewall-Regeln nicht, wird der Benutzer bei +einem Verbindungsaufbau der |AppName| mit einem Dialog der Windows-Firewall +aufgefordert, die ausgehenden Datenverbindungen zuzulassen. +Durch Registrierung der Firewall-Regeln während der Installation werden diese +Aufforderungen unterbunden. + +Für die lokalen Verbindungen eID1 und eID-SDK müssen (unter den gängigen +Standardeinstellungen der Windows-Firewall) keine Regeln in der +Windows-Firewall eingetragen werden. + +Die durch den Installer angelegten Regeln werden in Tabelle :numref:`firewalltable_de` +aufgelistet. + + +TLS-Verbindungen +'''''''''''''''' + +Es ist generell nicht möglich, die |AppName| mit einem TLS-Termination-Proxy +zu verwenden, da die übertragenen TLS-Zertifikate über eine Verschränkung mit +dem Berechtigungszertifikat aus der Personalausweis-PKI validiert werden. +CA-Zertifikate im Windows-Truststore werden daher ignoriert. + +.. raw:: latex + + \begin{landscape} + +.. _porttable_de: +.. csv-table:: Netzwerkverbindungen der |AppName| + :header: "Referenz", "Protokoll", "Port", "Richtung", "Optional", "Zweck", "Anmerkungen" + :widths: 8, 8, 8, 8, 8, 35, 25 + + "eID1", TCP, 24727 [#aa2proxy]_, "eingehend", "Nein", "Online-Ausweisvorgang, eID-Aktivierung [#TR-03124]_", "Nur erreichbar von localhost [#TR-03124]_" + "eID2", TCP, 443 [#eidports]_, "ausgehend", "Nein", "Online-Ausweisvorgang, Verbindung zum Anbieter, TLS-1-2-Kanal [#TR-03124]_", "TLS-Zertifikate verschränkt mit Berechtigungs-Zertifikat [#TR-03124]_" + "eID3", TCP, 443 [#eidports]_, "ausgehend", "Nein", "Online-Ausweisvorgang, Verbindung zum eID-Server, TLS-2-Kanal [#TR-03124]_", "TLS-Zertifikate verschränkt mit Berechtigungs-Zertifikat [#TR-03124]_" + "eID-SDK", TCP, 24727 [#aa2proxy]_, "eingehend", "Nein", "Verwendung der SDK-Schnittstelle", "Nur erreichbar von localhost [#TR-03124]_" + "SaK1", UDP, 24727 [#aa2proxy]_, "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 Anbietern und Kartenlesern sowie Informationen zu neuen |AppName|-Versionen [#updatecheck]_ .", "Die Zertifikate der TLS-Verbindung werden mit in der |AppName| mitgelieferten CA-Zertifikaten validiert. Im Betriebssystem hinterlegte CA-Zertifikate werden ignoriert." + +.. [#aa2proxy] Oder ein zufälliger Port bei Verwendung des |AppName|-Proxys. +.. [#TR-03124] Siehe TR-03124 des BSI +.. [#eidports] Port 443 wird für die initiale Kontaktaufnahme zum Anbieter bzw. + eID-Server verwendet. Durch die Konfiguration des Dienstes durch den + Diensteanbieter können durch Weiterleitungen beliebige andere Ports zum + Einsatz kommen. +.. [#TR-03112] Siehe TR-03112-6 des BSI +.. [#govurl] Erreichbar unter dem URL https://updates.autentapp.de/ +.. [#updatecheck] Die Überprüfung auf neue |AppName|-Versionen kann deaktiviert werden, siehe + Kommandozeilenparameter UPDATECHECK + +.. _firewalltable_de: +.. csv-table:: Firewallregeln der |AppName| + :header: "Name", "Protokoll", "Port", "Richtung", "Umgesetzte Verbindung" + :widths: 25, 15, 15, 15, 30 + :align: left + + "AusweisApp-Firewall-Rule", TCP, \*, "ausgehend", "eID2, eID3, SaK2, Update" + "AusweisApp-SaC", UDP, 24727, "eingehend", "SaK1" + +.. raw:: latex + + \end{landscape} + +Entwickleroptionen +~~~~~~~~~~~~~~~~~~ + +Die |AppName| verfügt über sogenannte Entwickleroptionen. Diese bieten erweiterte +Einstellmöglichkeiten und unterstützen die Integration eines eID-Dienstes. +Die Entwickleroptionen werden standardmäßig ausgeblendet. + +Aktivieren der Entwickleroptionen +--------------------------------- + +Um die Entwickleroptionen zu aktivieren, öffnen Sie im Menü "Hilfe" den Punkt +"Information". Klicken Sie zehnmal auf die "Anwendungsversion". +Versionsinformationen. Nach dem zehnten Klick erhalten Sie eine Benachrichtigung, +dass die Entwickleroptionen aktiviert sind. Im Bereich Einstellungen befindet +sich nun eine neue Kategorie "Entwickleroptionen". In den mobilen Versionen +erscheinen zusätzlich Optionen zum "Vor-Ort-Auslesen". + + +Erweiterte Einstellungen +------------------------ + +Die Entwickleroptionen bieten erweiterte Einstellungsmöglichkeiten, die +nachfolgend erläutert werden. + +Testmodus für die Selbstauskunft (Test-PKI) +''''''''''''''''''''''''''''''''''''''''''' + +Die Selbstauskunft ist ein fest integrierter Dienst der |AppName| und kann +nur mit Echtausweisen genutzt werden. Wird der Testmodus (Test-PKI) aktiviert, +nutzt die |AppName| einen Test-Dienst, der es ermöglicht, eine Selbstauskunft +mit einem Testausweis durchzuführen. +Der Testmodus (Test-PKI) für die Selbstauskunft kann auch ohne Aktivierung der +Entwickleroptionen durch zehn Klicks auf die Lupe im Bereich "Meine Daten +einsehen" aktiviert und deaktiviert werden. + +Interner Kartensimulator +'''''''''''''''''''''''' + +Der interne Kartensimulator ermöglicht die Durchführung einer Authentisierung in +der Test-PKI ohne Ausweis oder Kartenleser. Beachten Sie, dass in den stationären +Versionen kein anderer Kartenleser verwendet werden kann, während der Simulator +aktiviert ist. + +In der aktuellen Version ist ein einzelnes statisches Profil hinterlegt, das über +die grafische Oberfläche nicht geändert werden kann. Lediglich im SDK ist es +möglich die Daten über das Kommando SET_CARD zu beeinflussen. +Weitere Informationen dazu finden Sie in der Dokumentation des +|AppName| SDK (siehe :ref:`Software Development Kit (SDK) `). + +Entwicklermodus (nur stationär) +''''''''''''''''''''''''''''''' + +Mit der Aktivierung des Entwicklermodus werden einige Sicherheitsabfragen +während einer Authentisierung ignoriert. In Entwicklungsszenarien, in denen +ohnehin mit Test-Diensten gearbeitet wird, führt das Ignorieren der +Sicherheitsabfragen dazu, dass eine Authentisierung erfolgreich durchgeführt +werden kann. Auf jede Sicherheitsverletzung wird in den internen +Benachrichtigungen der |AppName| bzw. des Betriebssystems +hingewiesen. + +Die folgenden Sicherheitsüberprüfungen sind im Entwicklermodus abgeschaltet: + +* Die verwendeten TLS-Schlüssel und ephemeralen TLS-Schlüssel haben die + notwendige Mindestlänge. +* Die URL der Beschreibung des TLS-Zertifikats des eID-Servers und die + TcToken-URL müssen die Same-Origin-Policy erfüllen. +* Die verwendeten TLS-Zertifikate müssen mit dem Berechtigungszertifikat + verschränkt sein. +* Die RefreshAddress-URL und etwaige Redirect-URL müssen das HTTPS-Schema + erfüllen. + +Der Entwicklermodus ist nur unter Windows und macOS verfügbar. + +**Wichtig:** Der Entwicklermodus kann nur für Test-Dienste verwendet werden, +eine Verwendung mit echten Berechtigungszertifikaten ist nicht möglich. + +CAN-Allowed Modus für Vor-Ort-Auslesen untertützen (nur mobil) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Aktiviert die Unterstützung für den CAN-Allowed-Modus (Vor-Ort-Auslesen). Wenn +ein entsprechendes Berechtigungszertifikat vorliegt, muss zum Auslesen die CAN +anstelle der PIN eingegeben werden. + +Anzeige der Berechtigungen überspringen (nur mobil) +''''''''''''''''''''''''''''''''''''''''''''''''''' + +Überspringt die Anzeige des Berechtigungszertifikat im CAN-Allowed-Modus und +wechselt direkt zur CAN-Eingabe. + + +.. _SDK_De: + +Software Development Kit (SDK) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Einsatzmöglichkeiten +-------------------- + +Mit dem Software Development Kit (SDK) der |AppName| ist es Ihnen möglich, die +Online-Ausweisfunktion direkt in die eigene Anwendung bzw. App zu integrieren. +Damit ermöglichen Sie Ihren Benutzern die medienbruchfreie Durchführung einer +Authentisierung - z.B. für Registrierungen oder Logins. + +Das SDK bietet Ihnen dabei den Vorteil, die Online-Authentisierung durchgehend im +eigenen Markendesign durchzuführen - ohne dass die Benutzer die gewohnte Umgebung +verlassen müssen. + +Das |AppName| SDK ermöglicht auch die Integration des Vor-Ort-Auslesens. +Hierbei wird anstelle der PIN zur Freigabe der Datenübertragung die CAN +übermittelt. Diese ist auf der Vorderseite des Ausweises aufgedruckt und wird zur +Freigabe des Auslesevorgangs benötigt. + +Integrationsmöglichkeiten +------------------------- + +Bei der voll-integrierten Version des SDKs wird die |AppName| als AAR Package +bzw. Swift Package in Ihre eigene Anwendung eingebunden. +Der Vorteil: Die |AppName| wird direkt mit ausgeliefert, sodass Benutzer die +|AppName| nicht separat auf Ihrem Smartphone installiert haben müssen. + +Bei der teil-integrierten Version des SDKs wird die |AppName| im Hintergrund +aufgerufen. Ggf. kann die App jedoch trotz Teil-Integration mit dem Installer +ausgeliefert werden. + +.. table:: Integrationsmöglichkeiten auf den verschiedenen Platformen + + +-----------------+------------------+------------------+ + | | Teil-Integration | Voll-Integration | + +=================+==================+==================+ + | Windows / macOS | Ja | Nein | + +-----------------+------------------+------------------+ + | Android | Nein | Ja | + +-----------------+------------------+------------------+ + | iOS | Nein | Ja | + +-----------------+------------------+------------------+ + +Entwicklerdokumentation +----------------------- + +Eine ausführliche Entwicklerdokumentation des SDKs und eine Auflistung der +möglichen Fehlercodes finden Sie unter https://www.ausweisapp.bund.de/sdk/. + +SDK Wrapper +----------- + +Sie können den SDK Wrapper der |AppName| zur Vereinfachung der Einbindung +des SDKs in Ihre App verwenden. Der SDK Wrapper bietet Swift und Kotlin +Bindings für iOS und Android an. + +Informationen zur Integration des SDK Wrappers finden Sie in der +Entwicklerdokumentation unter https://www.ausweisapp.bund.de/sdkwrapper/. + +.. raw:: latex + + \newpage diff -Nru ausweisapp2-2.3.1/docs/installation_de/conf.py.in ausweisapp2-2.4.0/docs/installation_de/conf.py.in --- ausweisapp2-2.3.1/docs/installation_de/conf.py.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_de/conf.py.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import sys +import os +import shlex + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +needs_sphinx = '1.4' + +# If true, figures, tables and code-blocks are automatically numbered +# if they has caption. For now, it works only with the HTML builder. +# Default is False. +numfig = True + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +locale_dirs = ['@SPHINX_DOCS_DIR@/locales/'] + +gettext_additional_targets = ['image'] +gettext_location = False +gettext_compact = True + +# Add any paths that contain templates here, relative to this directory. +#templates_path = ['@SPHINX_DOCS_DIR@/_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = '@PROJECT_NAME@ Erweiterte Dokumentation für Administratoren und Entwickler' +copyright = '2018-2025, Governikus GmbH & Co. KG' +author = 'Governikus GmbH & Co. KG' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '@PROJECT_VERSION@' +# The full version, including alpha/beta/rc tags. +release = '@VERSION_DVCS@' + +today = ' ' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = '@SPHINX_LANG@' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +#exclude_patterns = [''] + + + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = '@SPHINX_DOCS_DIR@/../../resources/images/desktop/npa.ico' + +#html_theme_path = ['@SPHINX_DOCS_DIR@/_themes'] + +#html_theme = 'appcast' +html_theme = 'sphinx_rtd_theme' + +# If false, no module index is generated. +html_domain_indices = True + +# If false, no index is generated. +html_use_index = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +html_show_sphinx = False + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +html_show_copyright = True + +html_scaled_image_link = False + +# Output file base name for HTML help builder. +htmlhelp_basename = '@PROJECT_NAME@InstallationIntegration' + +html_context = { + 'display_github': False, + 'display_bitbucket': False, + 'show_source': False, + 'html_show_sourcelink': False, +} + +html_permalinks = True + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +'papersize': 'a4paper', + +# The font size ('10pt', '11pt' or '12pt'). +'pointsize': '11pt', + +# Additional stuff for the LaTeX preamble. +'preamble': ''' +\\usepackage{lscape} +\\hypersetup{pdfauthor={Governikus GmbH \& Co. KG}, + pdftitle={@PROJECT_NAME@}, + pdfsubject={Erweiterte Dokumentation für Administratoren und Entwickler}, + pdfkeywords={installation, integration}, + pdfproducer={LaTeX}, + pdfcreator={Sphinx} +} +''', + +# Override tableofcontents +'tableofcontents': ''' +% Remove header of tableofcontent +\\makeatletter +\\@starttoc{toc} +\\makeatother + +\\newpage +\\pagestyle{plain} +\\pagenumbering{arabic} +''', + +# Latex figure (float) alignment +'figure_align': 'H', +'sphinxsetup' : 'HeaderFamily=\\linespread{1.9}\\sffamily\\bfseries', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, '@PROJECT_NAME@-@VERSION_DVCS@-NetInstallation_Integration_de.tex', '@PROJECT_NAME@ \\linebreak Erweiterte Dokumentation für Administratoren und Entwickler', + 'Governikus GmbH \& Co. KG', 'howto'), +] + +# 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/npa.png' + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +latex_show_urls = 'footnote' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + +rst_epilog = """ +.. |AppName| replace:: @PROJECT_NAME@ +""" diff -Nru ausweisapp2-2.3.1/docs/installation_de/index.rst ausweisapp2-2.4.0/docs/installation_de/index.rst --- ausweisapp2-2.3.1/docs/installation_de/index.rst 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_de/index.rst 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,7 @@ +Inhaltsverzeichnis +------------------ + +.. toctree:: + :maxdepth: 5 + + README.de diff -Nru ausweisapp2-2.3.1/docs/installation_de/metadata.yaml.in ausweisapp2-2.4.0/docs/installation_de/metadata.yaml.in --- ausweisapp2-2.3.1/docs/installation_de/metadata.yaml.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_de/metadata.yaml.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,10 @@ +pdfauthor: @VENDOR@ +app_name: @PROJECT_NAME@ +pdfsubject: Erweiterte Dokumentation für Administratoren und Entwickler +pdfkeywords: installation, integration +author: @VENDOR@ +documenttitle: @PROJECT_NAME@ Erweiterte Dokumentation für Administratoren und Entwickler +subtitle: @VERSION_DVCS@ +language_iso639: de +language_babel: ngerman +tocdepth: 3 diff -Nru ausweisapp2-2.3.1/docs/installation_en/CommunicationModel_en.graphml ausweisapp2-2.4.0/docs/installation_en/CommunicationModel_en.graphml --- ausweisapp2-2.3.1/docs/installation_en/CommunicationModel_en.graphml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_en/CommunicationModel_en.graphml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + Internet + + + + + + + + + + + + + + + + + + + Local network + + + + + + + + + + + Local machine + + + + + + + + + + + AusweisApp + + + + + + + + + + + eID-Server + + + + + + + + + + + Service-Provider + + + + + + + + + + + Browser + + + + + + + + + + + Update-Server + + + + + + + + + + + Mobile device + + + + + + + + + + + Third-Party App + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eID1 + + + + + + + + + + + eID3 + + + + + + + + + + + + + SaC1, +SaC2 + + + + + + + + + + + Update + + + + + + + + + + + eID2 + + + + + + + + + + + eID-SDK + + + + + + + + + diff -Nru ausweisapp2-2.3.1/docs/installation_en/CommunicationModel_en.pdf ausweisapp2-2.4.0/docs/installation_en/CommunicationModel_en.pdf --- ausweisapp2-2.3.1/docs/installation_en/CommunicationModel_en.pdf 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_en/CommunicationModel_en.pdf 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,466 @@ +%PDF-1.4 +%âãÏÓ +1 0 obj + << + /Title () + /Author () + /Subject () + /Keywords () + /Creator (yExport 1.5) + /Producer (org.freehep.graphicsio.pdf.YPDFGraphics2D 1.5) + /CreationDate (D:20230821114745+02'00') + /ModDate (D:20230821114745+02'00') + /Trapped /False + >> +endobj +2 0 obj + << + /Type /Catalog + /Pages 3 0 R + /ViewerPreferences 4 0 R + /OpenAction [5 0 R /Fit] + >> +endobj +4 0 obj + << + /FitWindow true + /CenterWindow false + >> +endobj +5 0 obj + << + /Parent 3 0 R + /Type /Page + /Contents 6 0 R + >> +endobj +6 0 obj + << + /Length 7 0 R + /Filter [/ASCII85Decode /FlateDecode] + >> +stream +Gb"/LbDpC!P2-%c7K,Q-pjOd%&g0GtegtDb">Ec4b^isoIm>hHGENP`],HC'*b\EakZa?8E=O'cJ'm'i ++(>-:,Jbh^Y9,(N=i[.pm3eMj?=36uj-LkfqVsfkHiD3.7V_.E\p@-fbIa8Wh7NWq+]`,5Ar6rEa!TuWg,HZ1X +--5%Bg]#*gruitWRX&?=`B-7JG[#dW8eg0/PXfusJGnH9p#tcpCS5CU%FkDS?GH)q5Q%(G5$EoYID#Q3 +aoC&O0iT5c7`W9h<+c[$gXNtKr8m\hf.*/Z,$J4mV"?o2H%p1$_>7 +S1`,HCB(Ej4+-u#71\m'O +UZC2G*fo)l=RENWn1c">`OJa3]miHB\i>$8=Lcq.[EaW[eC0kU2)Qg7e5D0K2Ei&:4+$=-*O4P'Ac6+P +n]r2=Sd!uG>&S:1I)GPQ6G)_h['I]%?HouVSW1<42(b'Ul72\$f6iNIS;S(8U4IcH\YaAL\]EUSo>g,m +D9XeA&V)A1B_/\/0N)'")ST6]dHYN]J*COGPunl%DE))7q0X1`W(sL*.X9^bWNSOa`sd>RL'k;bCSNaY +r[dQSs&[E6IcN2tiP"7Er&\e`$-KIJ88C+bmsF=)TBFtoH233@p[nAF"p*e4iAI(4L/Ab<:7s>mf\.R& +SqWb\GF@E)D2V$fXZ%mi->-nCWb7/IQ*a'+SGtJpSk_<4fmd.ok?>!pVLDB'->U&t>%r-*G#jVF.6`'] +rqo)3ear)@*bk9Fm_u1AjY'RK0h"oMA1nbPX^3V8arG?<^_Ud<%&6A2cm$jTodoU,YuCs3F20oI>_kkd +WWt6Gp$>qbTW_?EfV5E:+_mpp!Am\q[/(`H2g275dN?$%NNuB=go$;3G0N\$=Nsaq6^X^%SX4B?N`LWu +[D9Jkf3Ud`\+J&@c5/rXX`r&1[,f4U&;-8=J*q!Gr; +ekN:!3IU;$?@Y:g$\<;p-#tuf#c;8D.V4^euEk&!a6_q.MFoLQA0SlXD` +)o6f'=^3*[k?87i2@`O+Ms_+jbj1b#3`!qd_fC"U_KdEiBa\?DN2J[VSn?W")Lbi#o&MHo=Y +)XF4h&+W+8"]DB[.URZp>*=V*/FRbofMs>12u8e +7rp+P7Ya\G$ebN6k#.*&C,TF1%A:8oZ;;;Q#BP+MRZ$.,.?r'Tqi3V5Yijp6em)8%b$(jJ\JRNV0CDs% +2gi?;kMPg\>T?CX=_QmY]#gBs>>8fa$hr#Of!,W$dp2BJaY.?\Nd^*M\#)g$79ldUX^/TBc'*lYJdF`$ +@[ReB_^bNKn$-AOa*/ar"H9LkHRaa5>ZsYQJ,&Y#XCYW,M`NG>nW(p+I]($^X[b)Ce79H:6A8g^&``q/J0?:pruTDGpkg0@t;4ZWH +Q]\a(PY4s=+Q-t)Khc$CD[6F44PHJr2_@_R,oD>m4Dp_]PG_]6*1Ld\[XjCZ"m9U)P +jh4I(1el3CD"]1#$k[+a0D*hlYu"0`!sC-]=3%RgD>gc4*kh3tbUIYsd# +R0X\]fE!CQcu'7DAT4$Y%Qo`LnOjd7rn\EodB9Z"d`cA]X4qV_Xo>cU01-&YGIK3S.SV.4M.!u?^*f^/ +6PC;Oh1X.qdh20gqO.j1dK@7T:pTV^^(F)Y]+kNQTudI2(0cp$D<]UI5IlX7dclga?pM*(F>$!o[1$QG +dA,LS"(QE;[LsZ.-dTSZWb9V&h*n +?RiVRehmV/88HZ+48/u-&9?$;\n^Uo0J#MLWRcnif7'4nFoA^R3s6%t]jh]1CfHAZ,&\=rC%Asu#73Hs +c]5QgcU+jUL\SM*)_7PW%sD*U<1t<[>.JTjLfS.3_EWeMgF:gqf"o`7/^np;fS7s;ah"T+)&2At=Z-;auX5R)XKXsW?r)ATLfCD;>a:dT,B"$P(WA:/WOXVL^iM4:<%fORR5pXd+qF)a<\`-/hbZm\.UFH?M>U7W>MsHO'h"fMl3%SN>mS +JlA=Lm\(g_@IBh`+e*i"cuop;4pKci*b5&$[sQ3#\VJP4ZJ@le_pGN$mNEG84749:gMm`1hsMZSg=nan#BS\gTe%LT`]8+DcFCi(>-?TW=%ga9N +&ENXHm\&)F$5`DZ&&!Ds?`qb*0DA:;dcuL$o_f6n:lS?hgc\U_I*Fg]&7%kJ%ln\)OQ#SeWPMiJYBY*qHi>%( +pX&1QN2Z+b145Yu(QI>Aq-GgHodene+ieU6a=JuZkHX +6iAIoon^5t/3WXWQM(qMDJMA0\4,.%?$tD$D0F8UMma@X:U=^T\JB)8/6`$n\o#R`Q=G42X2F=&oI+RG +(&`">8>S6.jS)\GQ*Pm"f@nOAhMst/ia;)(XP,4/6ecd+/;oh/b\ki!^$Sq/phH78d@s-Z],4o0YA"9/T>g:+ +K?0oM]]-&9/3N,]Vq07R3L74-*J[)DXRF#6!2m5f13cL%:oAJHa)m(10ccj_CS&RgI.MNCG8t#XKDkJt +6CHuHR4C]BdFfFV?0MLS^.R/VUL.Jm?m[]rSKqPVLfL,6Pla/:#rk_Is`Q*-+NV>r,' +7rr4dq6F4uY=/(oD9;18[si^KccuT70k"25Ku,t?p[tAX[EsIfjmlpI:_]Yb8f=Fh/Q_.XSh=+& +oT.Pab!D1@N[7cb?k2#S$GJ:UOXkNU=-kEFc+#&iNI9]N.lGcR[,oHM-ZkQDr7NAd%2RE*Eb0Yj +mRgruneL>a*oJY.t4d\4)T3E"$gN;m*oASs.>4 +6X-]1hK$T?j9hF"GtX<83eGNi?l^n&.hAU)=6Lt`8$9WNXU&R](&V*3!kD3mGeH,&=!.9-?PqZ\)QM3X +,uWk5h*+9L8`l&oMIVu'>gIZUb/-hr&72Ea967YL&D^G@=0Q/9\m-\g-A'F+K-XM<9<*1RL"T +7RcZrj9kUIj>A1fHDL(uHTq52^tbj*jiZoA3?.p?[=5qT?VV;Y>s'sn4dS-SR:e/2\sK0,Cj;X1VHY%# +gu:#(('f9&o5fI[TGM&%^\M@+(* +d1c+l\#qnLL7T1$68>lXEF@AO[Q#&>.@+*VL:5([=;FDlbVG(Ymk'mF;&`*R/.V;c%(@\4?dgW@EE'GV +[lq#nPBI,gZf]*q0cSGEO[jKNgi8kp7H::K6>>71mRBj^@,M''h +`T7jVA$:'V*e0.OF<#SQRpcQcXjWB:"*?N`H$I@3F@5p*h=*E-.U/senNK2sC^9/hZs7m,^/1^c?jGm' +2`].!bSM`k:>ul]6J255!52r](N3S8U\EaJh<&PrTGtTUU;ic$o&]WdFY-aO"*[0L,Ln+ +5">WRIXjAiWPPf96Ur,FF_^-uAaN:r%TM_4C;>Bmg&Vj-51aeQ!M^0F[q\!N^8L,\jEh5@CH9TsnSeg< +8B\DpF-)N_Qe4JC97i2iLi)+o[l30^)BMY)K9XbZ3(4(EWub$`#M6o2.3*UmKjb@e^b;+Da`*,k.Q(*3 +V\nnLAIk[UJ^Vt'rt3B?[U$+,j4Y&JTl;h!Vc`!)U]_('=L_9_AiKs+M+YkJT3-N3GP2LB +-qbK;'M()G!>>_ZfC?K>ObMc3nl@h!=#e1OG,0mlN5,PC*DWkg4n?mUq/gF=&K&9]Rq%?":>FeB"3Sc# +?%("Q[#]l8S]a*"riZ55k;$]V!67]Gr!jT'@2CGi/M^FhM*B +)NhH^=ZdqW,h]kpI(2cr.;/LL83q?q+3raO8#1toq/RI2e;38PM;JNj[(8>cjA^]U+2.#cj2 +'CO*a_k]NCUBbRu^%B)oA-RO7ZsC-#`KTIb5k+&7jLmX,T6U;_SCZi)E5kWu1(bsBHXUSRn,];,1%h9E +VLodNL1^t5K?la?4T-XPaeUj(oa[qlh*HJ7qJPjf(O`imQ1NZdK(35'Pj[QNm&[Z?3LC'SnTe?VRXE\j +oe?Uis83#gPt7VF?e3sJXbJ%b+\i&pUjB`f-*7:Bg5V6D`ja^OX/G37D^ms&r6BEVjEGCR>DL5NE%XDm +j6Y.]\T"isWbdAPMOD]oES:#diS=&PXXQ'Ul_A,@FQLjcdE;=W[/M#!`t5!JK'2;>KMD9pKqi5a(*cq0 ++rZpC''q^%-u!X,\@Z.RX&%!&k%1@;g@UYd*$?C''"Ofc[VMItG4rrF>n]0a+rid\Nk>-Ip"elRk+\Hu ++tCd/RgV(qO5X6KcC1Z,3G,uO_GtD7b\&Jo2t[Ir'=B5F,0j0^[a[Yf[< +22Ms8IDr^Hdo-k!aI3EN-,nd=JR2K6T,_1&_XNu"4Z0WW*3[?@.Pts1?lY0]m9.&Lj/W'lT5Tc+me/ND +q>*3KLO9U[*7WOY1X-B0;hOb@>hjXb-tSA<3V][\!$c%CEYL.Y1QYGC+e8@!M,eW0'nm%F$;WZS'o:$j +HI["_VL9F\cXLQ/)n"t1kBLSQZroM=IpuQTrhUfS=RAl#V]]4q@lk]]'ESfWBK)agNlZp2f +#L0,/#rnnOk%/pXiYjbVtt2@>\]EocfV +@)sk2WGQpgqCq4]Y;2'D94"b"PKQO(-.CQnY8k*c3]Lkdk1K5SR82`3.S]SCnnNS1LFnY8V_,U3J*;48 +)RjNeX-&Tpl1,1X--@O;*4rjm)3-;PnE-u%'';Dbnd9HcG,nSTQWFDL+-5B[=cIjQ/$*g&j:tk0B! +r[E1^IEO,T$H\XIb/R4&YB"eH!/YfZjHX5s/,V/=H'N4O]=d\u87#_2%Y&r\&#q*9V9PJmOAIu*CQ^Qu +brM'B"0$sU1NQnJl7@C#r?&e1ji3)`]d+([U6HU@8tCS1 +@PX/]#K4;+ROlHGS77VIEAT6-AXG"T7'=RMGJ`#T`"DP+UcAL50Fk+g/N.JX5`*NHir>8lRm%Id%mr,Y +3[umMCdh3K+^PctV(dqJ`CN].7j#mM76gOZBHB%;p-^]s>81liBn5^/>:[a\e'$LWp9qAP;>3tcg-HIC +a[TffY-YNL4_E9$Os>sbpX<;7?28&"1aQ,g#gVRj2@OM-W+8iaZ.$_0[;O]sH#(oF7<)kC.0\o-guc5V +8dc5JeE2u>g;!_TgU'i&-5LGXEnYI`g;#B5VN(86R4m9mAu1;A&S2YZ=cTFNgr!IM`*90Fg!tuZtVfNb3?8[d:[nGdADoG=).IKK[2;43RY`rZR?5=KE_th +>s;XV"P%oeS2;sp1([V43s;sRE.BPj#c%f<$q`!k,^Dkg2Ap"8-prONfd89N79X^+/jM.>:Mf6gWPrH)k'EV/BjUg9&=Vqk[H'rl0YR2s\>C`+9[Sul/AkUV+fOU@QWfJm;jq+J&] +LYaQWW:qKL5o@u[d$?M,+V<%A76EE!*I8C\:LSDt&JC7p*U;j6`_K4]5\Q"]etiIu*K9iAR#O/6PM\*e +k8RTHhUIq*]!1F%$^Z&/kf!knFH&7/m\i$=q5d1oVES:K:Ro'lks@ +Z$DdBIg-gc,SUr9ZsKBl=i7N79u6&0^7sGafU+"XRdK__3D0Bm=6tDXN\jeLa]mlU%EhPf\Pa20HV;im%Iqmr2&d?iXA86H]2XjS +[3P/=8rhQ!.\S#cqN0UXO9dGPMD@6Z;?_?_\FldRYbNg-MXE" +BLVAIhC6^FD,2c@P;]iq'%u0HrKMPM8gdGN&$cM;RIoG#$dq^p'Gm'Id`pZ"c,>`[-3m0W=a_de'fWHn +$N"M7:!c&WFDK9]#D>6YlBSQ[I94KIPLeQ^.a=aop\7?:E@h%ij$1+FRr7+V1MK3("kMC5->0IJq2XYc +?QS^?K?[jV]kGXYfIY+lX7&?=dQg>qr@f1qo]E'R;t6k^P$tg0>N8C6bu7p8fkFbCeV7ngbn?E-IWP); +RVk$kk*4`UjY\8(6aEHi,`GB'Jb%GWk(8,Ij6NOZ5?CZM%@L%A"XpT&2<#lSmP0A,$oE +p+B+-cgDcrHK0?r1k"1`X)np?h,4*o;1!L$SS`02NhVffhK@n+fBm5U*k6TeRUW`)6 +efh#q^$BHVp?nm2?Ie>[I-k4[g4?#W:M=r]G4o7m9V)n(pA+RHr>g?47]-DeWahq``%I:H@,JS;TCH<% +bcGf!GKuq$qqbVgf"8?_]lt82iS(o(IIH1[YkKd,I.ltZ=sr4>kX@M68[b>dY4piJY2d3Zrc"LM?^C?- +AKJmDs'^6iOJ::jnKbd-o7MOEgZ+@^hQM5hU?EZWM4N1Xa29Z#:n@tc$fg>FW4_=R#@s\?(W.XE9'n)X +M?E%$M[,ns5,3+cd"@RsionMQ.YaOs8bNruAce*40$$nX1pX+pK?"AFo,*DB_*'^!d_lP!QHnVGM&Pa@ +,\\893j8;/k+r`AQpE`p#_;n/f""fBA45.?'6aOX)_7[!a%2XH`_@6r.tONN)o;q&aV\@L(GgUV*n0d/ +6]54QnQa,>"LYDX0UCG,Jf>r/PnT%^L(S*PYt"TWf631Y/Te.087TWCXAro-#^aAqGjW?Q16toY9,MZg +JR_F(]qG2@`-I5R/K&kSUj[/Po7YJ7Dl&M.otMVB"LNkip9'!I3o#;`XA?8Kg_kB8iLlL$J`(dmWb5J, +5-fuH,UZ(l[u*U+bY6!q%&qtOmat\:V;*$1I!&1ej-)8IB`rW3fne7 +B/)PKLng!9;.Lt-4Y+aN;^pDF.AYDe:bE!UYV:B9?4dfM3T&GK]_-,h!PCYek,];>:_[:3qfiNmV:oI<'VeK/kB%0]d-dBbB^OqMiROFXB6ked)"&C6/,.fe.S +[@GkiM(qo4UHnj9>*,F*`tm]LV'@$a&[:.*"6\7-@aOCJ;*UF*h:^L4A"d=p$.2V9mFcA[oCOi@55"i2 +Q-D"n"6r664q7=5aW_UZe418.;QJ*s&ee8nX\epZg?PAhgB[*=*,P<5`h/7rZ4&SL#eKIC;gU2KKLi/c +89&FArORK(In*ho@'ia^m[#.&9CI>E*4&XOn\DgWPl<_KKb=iTU9e.,%(lkUHso&k^NVfB"Zs"5T.gm0 +MT5$Ae:.loPd!C^J'0fa>c#7q1FinP.eJj98+N(Ska&V#I$j_GQk/k>,.C!)At?SI>@YlJHRK]\B;@Xh +:s*FDMQ^jOjhYHT"Rm*InoiM"I$miGN7Sl(+sHXrKs*FSEZ"K-]qf27!^,-@9$E*l7F8r1 +H?=I]BWKL.\QbumkA9KJK)/Ua._8,T2d`Mp$(n)25h[aG"R>4R'q4W_l0-67\m$%r#V+rK1eEu*+49r2 +=S.1*%%t]=Ka)t`9kON#gR.]9nB>X4S3X#7h`E[0g%q@>L3[N?CA+/dSdaro7a6j/_,0HFW/a4_ns-7L +?F$gBY>o16$KjjG;ctoJ08d:(73rQ]/$UBsXUjO-;?BjQQ2%OuU!2:)#Uce>[*%9>&u`s^[j=j.T8t6o +24pQfOJ+7WKO"ZDN5?l;NS;E*JWMiP[KKcIeh(A]IAo^g;h&Ku_I:g]6F"kqcg*PQ$1?KW8L'-.!#I(3JBZ2THNU-"*b/b.E,:nrC6&EUjCEdISX +od4r/Oh-9PUZ?**`aE@R%F508^@UCl5$WE`C&+jtf;AH8"tR+N9o[jFWh8c[Ki,I:[\q[FkOuPpr84+P +\;9=+\8V<0*_=3D2KSR^X[4LK(ME900o'OTbms3`l6o61;6]W%_@8@g!oJ[2K9E?%M[`_JP\,7qD1[fG +:AUXhk*9t[OeWfJ,<#I6*q0t/7JOsBSmM((iM3iC;&*)&9OJ[6f.DeSN_/GC+ +d`\bjo^]#[oQ'7H4l:RCqLA,OiHriH1k'3Q)`@Qak+TIQ<(Vk8Y=k>EYU#\;[U^#* +i2H541>amK.ShXS](VgY"SsTm-r+rt=_fpV',Jf[jh]^iDjB50IlVb2FP]+CI.FHN\g"&R2aat0jXV#L +YAYNV9D9@g]i!<)#B%b]]JG37r'#PKX\'LGE!5@66bcZ>+E&8UFF$@a5H[/mqm36cfZY>KV_`41 +[j"hBe]2n9dWO]]=%=*7_ltckCN/;a\(!+tPAsS\_8Q^*nAX +0r&@?'n:_Nd;=RJ2qK_>nZCP@7=Laj#ifA$65Ko&"G\C2_(3E8WT`(MsWm>lKf'7f1mEg,VJut +nobeL+;!1?FeV@_.'OhFSlO%@8e3If_I:\Vf<`\N*)3#U8Xg^iZg^^`7b%2s6LImeG1iZd\m+D(E/e&m +E9L',82nT%cu8N9?e0tH5dk7'2RBE(Q>khJH&gp,]MNkK>.LV;*bj,2]DU-/.DduP<=XQ)LL6K-]glm0 +,Hg^Hl@^t@rpZ26/CMBB&YYY[79XK7//YSg_N8eBm2E;+5t)0s+e7+oPRah'L?RIpGU30sn>hic;D* +oPs"4-Uk+5CBYX2Dh;<6bARI;nnD*!Q*aS!X/!)n9B0]fpiX@L-AG7hjS64AUagG&CTZf%*N,/7Qi!-D +GP("rQkq,-]_PY=bhRKa>99Bj&k?A_QaN0.MLOiC:>P$E9JBF^L(uG)pJ%P@c^3T#hQ.G*F8(`a% +O0FCD+0kf`IDkWo$i[Zb)9W(F`ugU*HqlKa&,WlSIdrHu3N"\5Pj\M(ktDH#pl&n2nY4'6aJPl +geLs5@j^:g->;*^.6]i?/r.Q+RqfbQ^bJ18S\^&LWn\F\b3i5E[1,O(?@gCT/jc+he-*pKUaS+@.4m,ns +9uCcp5FM7X4+(CZ +%+KF1HF#Kh?/i,8AhSR:"%C%WkV_,\gW&2.)i%`V-MeBiYFuIi*,EjrGZf+/6bU%.iQWs:Bc?+m]X]EZ +iL\+X4o>mpX5U??P.95rMYd&+Sk1)AB2la;Hh+Yr\Ym-op`mM!F&2KRS6"cl)Y^WZu4H +jjn6bB"":hGrsRtF/$9NF,$jJ&XG;p?hVsD]>'pJ4570$,4cPpY2KQL(WMj_L/Q:"D_]^,a5DpB:X*Z,[n-,!*ZU":p$9[p``''m+*)HuQ@(4T*Imtd:W/"B_snahN#]t!l; +VPi\=6TM;Uhcr1E_0tOuWgEtH=J)@\dRMQ<:%Fk;Y]CEjDh?X[MS+ARE^UY/:m<1S)^%Chj^9tV9%]]i +e%iI"oj9[4)]AmK+XNRg]TT>)_2.<7@iB*YB%nZ=`8Tn^YO$PgGKA57Y#+7^\$!h:okRW=-IQUfk5-XY +11&(tEFu?n:nFD70pQTIN;=dA:/b2/#AG]XG3lOVIeU,WfrM\ge!((_5l9E:G3.%3Se4teZ-KFq'U*Q* +.5+>R^$W`2-hrgXh%-+;WfoEqX2ltHqih9[V^%d?GDd*4QJpTBc_8p)g/?o;'_`TH:3X-=(UR83kIOGl*4`atf^)W; +EhBW;NU)f4?d8J4"[*4iPQb`*V^tF85J)'jBjbF'0[I/8]o%(AhZ5/afZiA\Em-0 +joFjk'[Q8kH^-V2=6nMQL\]nEFmN+gr]4W,o;pGVp8VQ-3][1fb(>*7l]_SARc1X)7u?DqpNQDoU/SBM +.teH%/e"h2a%,/>8Y0a4>\/6t^_]fkq!pFJ-L949lINHPCVUpL9DE1*V8s"1`hbW+:0!2)ZDMW8(\tEn +L>Fd?p&A]1_Z(Hn-Q"K.`OE+=<8.09ikNO#o?5ljpggk68;_&s)QH9QNqj%-Z.?fnJ0W4N!h&'=%pi>HY^_4Y&0m$ejKQc1Bo6@QCBdT$#%]#N-M6]oZ#fQsYquk%@O?#]EG3UX%\@ +<^'[a#`HuX=>?j0ji?Z<3:/@d^G]9ZH?Rb((U[JJS>El>7^5bKP00Ip,[.M?RF3u`bm/C7LNLaZ$H?&% +;K0eG?`B?ZJ1)o7U8fUkp5&&8A)W@[=e&H2r-9t0+YHINa*d6[5/k&2?9MOhEa"Ige%*K'bLI5oNJElp +>?To]Hn:nCQOOoAAUMTg^JcmaXYkp(ZA&q^T$?)a/F`/mqG,GE_mi#oW1OTJK@ +;C#>GGAGA\lIjclcOQ/!e4]_KFT!r"CNF5[IGMO?g$%C4%II!S9t*m>?/2h^UL[i&.U(XZ%7(lZL!dPf +R@>8:O.&S\!L7&n?)6pG7IDHFnfL5j&UeUsQFpoEUO(-+l+YlXh=dl*4#K?_gIoN5oCUXh>'$,(@R[i7bOEY_'P3fh.lioG.kecJ&cY\J$P^%ZdELl38Vt? +p&#j!mhP#kY8Hur.u*]"VQ3DHeoo""DCO;2/]D;rn9eLXJW=dl'*,'@c2*>1-`_N,N!)cq#Am,;[t5-++WQ9oT` +o^)>.MOP-`9i1@3\1'OTM1WDu;Z:sW?id'H(*,5%%7LOqZCD$ce8gr)QR$9Cc.,'BU+SG+o1@i +Z&use%c#b!Fp,d0;@aY-RDftu['$33CK7pb8>d2:X#b0#s.c/sb[lr+Q^A#+OKoT37Uhn65(bA:ZKYH4 +Sup^T=jKj,8;btLRXNA.0Bt2(?9\u!'[]^!$Ah)DW;&JLZeuNrBj*$%AFs)]Pmnr0q3[&L*>6t];S!#UNcG4Z._S&rf^m= +k.G7qn)D!C*SpW>/j&pVXBJn[l1tG@5(cLTaUVWhE*B"^T4GuM7aR8<,Iu>1ftj^8[d7toZ=>/*(L1#5 +Tm=5RqZ%!#=H7J?@;ENOl4GtQ$ga/K9^I;9<\?K1ON$Fh@mjH]/it%G!XqQt\9&QR[,A5WEMMCELMU8p +o&Vs-S"_XL=2;(V>[!/G4TA$o`]%2DFlcAh'A*r)TAV4^ccK]j+m9SOKi-TZkm;218 +g%DH8?/19UoY*?A:W(9#.6YTB0'4Ht]oTM,otHE%mh4N(he\aZ$^\2DB6UM5h)WO,ppkV3Pcec';c905 +pKB*)]^I9scF^LQ]j"KnIdc*PD=7P8]m'4?ZbIr-b=rSE(-KFa/=WQ9LXpt0],U40oS5pkk/78d*@5BD +#79NRK`1^jm*G\F65]a<^A[=[/\InC,8XDEqq=$90GSJ*@F!)NWKQFr)mMSKO3=9'dm!!srJ'uoh9*$H +fQYQWFM30DP[);pFK\s[oh*LOtBCLZ:6\-??Ee<2-l.N#Q6IE +9hN!@`)@./9<:C"s!@!qm.WtpZh`-C*-B(kc5]4'D(qqLdOd"iSlRoApFhpFG.j6TCk%`J(2.$QLmMt[ +&5N8%Y2>VR3]R_qqS9;g/0QJ$bhtY!:S2Wr7G]%LNCJ_LF%kb`&E)V_pWB]A3+c5VU>MEWR>HCUN+gUC +$afj191+&K%,4iDkhW#G^E/:OK;b_QUaC'K&%a!V;7tej%N^;8<[efFI'j]"/FGL`CNk'jJmfRd +eiq5S"E%B.)uO"TT[=7lO5]hg%*cT7S!kb9lTQ5IYK>53=.@2K/=*gBMDC&JTCp0:ni3aCrU%1K`TG!K +&i`@Q7n&Wq'3-)X7b$]>8U&U%pGTe2B"kq&f%X@Y=9.5X%a(f!['@j$^oZEujOsn3Ql+P^[(DT(5M,HE +Ok%q[L%1h<*Y,_8>s,]4A]15!c&qo?H28Kt2BM+tpkJUW,#FKCj*`VokqVbAnf,=>OFkK+N**f&MN`-H +c5V++Rkf(6+Em:UmZ++nNc%@Vdl<-B?)sp#ar'&6+)'*X/k"4aoW7!o6S3*9dloP+)H!i:ah9\_>s[J< +bmki-,J2p2S6!46\\k4VF7l8&i%ZnDX?L61BSVk*=B7l:&f8f-MT28@>dD?jfrJdB@ug9A8:@#qZngZp +qpW80'T>7FlFZ7O0"-(Gj3N@A%esb[Ef>$kO5NPb3TFhMgMHo\rm&uf4tlkBj//8lN9QhM0RFMcl'.?U +oQTLIoo#u9'GStm-VBQn[OrgJbU"6+M9uSo]HP$pIBBr_3ab32:BdJVYg@'[oEhLW7rjVtpWUq_K;0?)C@]b,8\M4k=NIZ#+-?V=[O`EJa3=AEX%F9^HT +,mg5jX4kpt1u%7q]pE)#Vj4\EfAIQ%'1,)+6go&eP'M0:4=-IZdV^b4K6GQo8MKP\_7k +[;O5?>0Q71NlO5iI.NTe)R(K#$!N[TP@gGPXHB=W5nbc)N6=gj+OeNCGM25GK#b\e4"@N@rE4[]\$P;QA=JWR5-U +ef^AhpMq*+Mr;HX1GLb^g"oq\I\0l7Y+:e6'2a)]Y.eZ#ePtq)GD!i_R^[`-3%q/ddbWpqhmVIS5PQgB.;s\$aSBZ\dLMe^W2H +;6/p`c>ON%LE!_,D(PR%Gi?,s@B51@euAW0r78-_Ai6\j*4:eYn!gGVlN<.<4:e?=q9C(/+&,0%C`.f8 +S&\;$KKhDM2ur[8hm#EGL\f9pn@nk$hlJU^LDM,^b@ES5nb^t>U:B'"Ogd4YMe%(QqtYZ?s+9o,qRK)H +`JiXU_slY]o8J\"'20]RT"Uks5nru/9i9)+rZ'`i2Hb50fMVt-3cpt9-r6LF1"u9J%#[kX&[I*AjpYr^ +#P"Aua$(u##O[In*1tOcJm(;Y[W&UK3-s#rBF0]8XbmM5D9Y7_;c,f?YEbK; +^YUJL3-#WrH'hspG".,scCK2[Q?i>l.(r\*nf_7tKr@/g0(9rSW[YZ&HhKn)W%Zfqm!lTMl*ba]Sj +Y0l[gBp;Q\hhsO`R=7r=7G.h"\nrfa])E4Y[WR\(d@ti^hhsYOC#.15o_2MgV_eFpB24iuIEfjQ8[QDE +CTUdi[P-@\k^mj&%6^-0%rpO0JS/A61$_edF>AmhrN,h4Y>:^j_S]LIHr+cHX"^%uH;i7\<-`0SNp9Ud +Ot_Ko6dYY/DK636iuD\d5@!HNFlsJbb8(m4mL)a%gTi[@\u:G)+tk;t%-X;@@6H4;IZ`E&9+\Z_1Hk:8X]' +;_4]*HujD;&:YaKW$(LjQd@&&=:Sk8?IA_EIRs4D3'M`j/@S(\W6S/lGVMs8S/ +V7Lo/c:%WANK;oCl;?\\?6aWMWuJ%+St'm]'6"T)8D.C2KbPV`qEM4<.fHG\J4V2ekK#0:T#dsfS=e%=-9h-[t]>[o;:9?;0'-= +e>#l]2tC[k]"'iaNGtFP6F&:r%e#98_>ck->Y%/F=>?BP2F=$p0 +C^0*Amf(tJFX-^^CYGn)m*#d:3KT9(jS7`r=g>N3VD!6mleu/QnVG2uZ$Bq8/_'UC(1Of:J686L>/q2NV$_*`s!8?$6YAKE`uqBdYhn-0ejV;n+e +R/h`pd8h`"b`O6[]1'ln-At?-4#RV=c5g9Ad%#[9H3N_RbKHnF@G;Su[c;YfE5(2j_kcK@ASr2'd=$UY +iR7llYJ'9IgAJ2KRp4J+SFme_10WdB2-nLA_sjOWQJJJ/MOcY/OmXXOBLPOr@2(2eg4LB]32Ll>8@`.C.0+JZsiR1=4?]5%KD@8 +#&\NF\l;V4MQM?.P"Y[h:$Z`"njs^93-*_,Cp_UDj[nqRD;)f%[*r2@9B[;AToFR![H`7ZD@Pc?)IG]9 +l@F-!TC&QWfL>Bs6oL'VjCB*)V_:nF-YOg]@aAY#WqD0FNQ`k^^]\\dWDKeSn&2_1m'c=>4*6XF6 +JV_-h4MN]E>gMtEe_SC>)SW0`Yp+=UD4PatND3B?Z`4W=0q?CJjg&dJZ`1$@"u/O=.-[%r+BebW26NUf +(M\1.):O6:?-aG+ZcqmjX]8BE=3<,WAe;N5s["S#%jaZrl%r#EQlZ< +2NamkZQEa&cFrIF&hr>E/W=+T'h$h9"\-JlkilS99\QH;MdUuHl.U-rDHsMF=iZK5A<"@XIUi'm&1S.t +Xg]gUH+.C(`=ZB-i\XY.2?T.B49j#nVkT7N\c=77s>A*%ORRu=d]KTB(Z +',XW>(?t3eE\TuSc@Zm4ANafkNkTESgMq5)eZtBJZt<_d(aJK1SJJnf9g\rDo`m4[SoiG1H:jA +X9P>17^]Gp=,5i4gAVW&OEa'867P)\8:qg6,2&@hn1VADCg:IHg`_&K]%K/"SqN@BMdaUCo0u=KI]&O" +Zmfj+_E&f.FjlXQko'_hKAe,rH!>-jGPph5mW_l%qTCYMEAaHiUVGs0;Qu*1>[-pf#Fq$YlB`l?ItU3( +uGT:.H3&E*Um +WYBC2C)J1jlg;:6,kASS!RRpc[a`+0$R6m7I@,$DP"4@/8+c_1E0e.@;6u9_lZ0%_.[j"R92[FM33hj#K +9k]`gMgAO>[qF:1Jqoukq2%)hK"CfZ:YaVDtjqq88DkuAG?b_4HUGh;^XcEL/NF?(A +5[$T*;;V9IMX>\\g#?b(FP^F#quR[#eI*ViV`89/&1hfl\@-VNqDYXjVNZ/P*WuKHSAJ'fj9>]UH.9qH +,"(3rkLk`s@$jM6WYp*B>2BPQ%')rpN&5RncRs'>'n@7p:DV:,%;*i3`[OmR]!JK4^K#Ua+(l%K4a(G" +'WN7Vo?,b%A_+aR7iuAebh=,LJJOk'Q*Ri6f,=Ns2ViG@VN2eJg5;Z%h"7`_C>u'V3<[/!I:f7PO57%cSfU`d+[m?1EP*_B +oV6)QK5n&0*HXYSTbrV\eS/$;($6'Gkn5#q:h&@sK8+RtUk\@C'3#V$.-;\V!qFu6'olF%Q\P%.%Y"UT +8DOd1'KlPMn%_!sg-&^<7U4CkU"KDrTT_*G^Y? +aLO?@T5^("[ph1mc9H;eEGE-3&Ss^;[o87D_H",3i3it,!"`@$3*VO^a)OJCGT7jq5-Zg*D-7h]FYh4j +\SFPY<7Kf*8[`/kfNUl=ACBI2oCgb +8VKf3o@1-A"oU:OCf+a"*lD7#<9K2^S;4a:SL^1S^BkqJn/#;8,k_SQgCNBI%^+CDD'1rtZdDsrTcqb\ +>`/q2Gg@/M"N=_MUG'6MpRr-eceaWE8(G.V?kaXZb:35M6*3,_2?[[PZtDi%ZY63KBd+>K_?,Tsl_:JW +5gMFLSe6:A]L^6kNl2!;!a',H^YK&PH7XJ$kUr*rNo1&?7*j^Ho^oC-<`BUu1-`-S\/AR.&h8=IG_8b! +mJ4QP<]3E(F7RPn5jGF3k7bSd=5a,TL(EtV,C_iE7o*/-d-dQp4G)6!$oPj+M"ll@M`2A)EU?[[&67Gj +o$NX\@RTdN>F6^W^[/PV,r"*pbd4T%6q@[CX#^]D"KP.C/oKok/(L`M^V!^K.eXra)f8`W=]U^Mrp-&3 +'?to@"2h4(Sh:G\/flN3f&kKDX%g09Aj*7b9qqpKN<^c2cH]JS12rJ2i<\#f,RYDXaD]V2HFhbbhb,DP +^g"FEYX(9Q>Yl1h9@+'Krj#RliB85M[DhtOj*B53E#`jH*A:2gYFoF#hM83)5d)/&#-6Pn#`E_kVR/Gi +D;Z+>`=6"ln4Mq?6"$2kl#oD7=]i)C`FACM$HS9?G7p__>9O]pJk+LU>P,].i8n;ZWrFi\^pf$V4QamM +XI56di?8m6'@n6`OTBG/QK?%-?Y(EDuYW`mdi;AEG:e;ZhWY?dKl6_6mF>DFd$-qUq:GW +-V"M.6QGW0)lMbG&)?7./!$bjX$8:`bn8,";^.uS<8"a<@,Gl%,g,;.dhsaY9(gcT,91YYp`,e#@oCQ` +l0FXtRFiFbV^cbf*/.c9/b.i^d+iS.8)E$JdT<;*hc([):S--5)BHo396FcIb**1&TdRj7@2o2.3-En#JC+^&.)G_ei(O37?!Fd8f%r=d)8m&9ldd=dF,p9,(?X2"n1'Q4E8NIOBi0[k;fSpVE#B +Asi+Vmj5G/p`H+K+F.9@'6N8=F_#"DZbW64b&\H&[mBdh];N.t%>XnBZ<rLWM6ZgW&9$r^Erla$"%NImtF\9U=jPZq(a@hM>+0au/]h@L_*%m3.b?"?d&OJGJ3@3Z"7aY"gOFf]c +T+X/)S^Oa5'&f1i>umjkS\!K7,sqEil)-ppde.)45^Cd'h5Lr8ukj4)--Gn*`0e"_D0`fc\fXP@AXA_0ftR8ql`?GeW-DB +k/#L`os>DOEDdo-F''^:Yq6#TIk0j,nQ-kYm0)(gag_7t<""iA+c1S\Te&;B_Wi=d+?A1<+0?)D(F +)#>l?/%J91("]/`T;ko?B4*:LM&^%8/Wg6"Q_M3@VnsF*ST'QW-:*q>(%#q*Z4% +OQL81e#MVN3+FMm8aj'55J"nP&t"k4\rQ&).n%XV@LomMQQajHBj=0/hotGO`qd'ifr!H3n7D5'JX-Yga"Sd*NLdM78+h-;q'nC;O)Y<(+W-r.@B>NYruCA37hpbn)s"P>u`ug%WM5hItN$]_AJ4 +?mZtbXC8%'6Wj.uQQrbA'mHm^#2-?2l(N!ke#I6CWV!:AI(*1\58;Wd-6VZHtatamgBltZ47E)NlDa&)%Rbt'deX@SO +O!F9rIa5T10fksne=*SIT'`8?_,H",2aq:A*Ts:Bh)K`E.uQ&AI80_$P^1`[;_hBl7a'pWX0#Wg`:D$g +ik__2B,Co<2CqL!\aD7%7[YdXLmaF$.KVR:lN]lGW=*ddGf(o*O.IrjQ;*8/a%gO+?Q*@W*d(!!h-Y*. +IB'C4Qa+`#e23LERBnX_N=S:fegf%]U5YreN=-sh=Sjg$H`Ljm=QR[:XQC&TEY0ge+L?6N*<>]ENC\8r +_mkQak?6?1(eZ3DP+20/9:VgeocoNj0'ekjO[SUc[RV>/$FWaFEKq/LVug#U1*s9ph2s[bPm_ddcZLXG +K!MjBRc1e_f^-L*C[$"q_;O@5L>iCrk4QgnAJ.M^mAsTo'mCBLmCfYD#31rZ0@k>VY^%,Z[&VRHA6s5t +RgQ7.Rc1%U#",603F:QUj*EV'7AstR16Zl*A-k4e(%;egc[usHuATa@eNVBHEQSdrCV7rcV"6Fc( +3SLas\dR'h=ir% +&S]Z[8!CXoO4Z,WethL$MO`+jVeMmRQ'"&T*+<1%[Mjg7pt2pVT1G.M;C8_,C!:c.Y"8^u_CXunL$1Q1 +2/0-s9Lk;l_=G;Tq&EAY:7="Q,5XJ[US$b]1T64S5W@u2FUulAWNJh]SO-$haJt;5h]&//IdP4'^L!?c +pJ*rA1jEd?i?8l.-j.XH!OkBejFj5k%C!KC8?ceUFBN7kD +)I](V1WD><^1b=((:b-S&/G_iN]f'I'cd^rL:2+(?kT;i;E"!AXZRM8e`-CpFBAXYGP)6QOM +g7%YOV'aGo(>#AXhKC_i;XSM[34ZmnRP_"<*)2kW +Tt)N;'I6ELcrW->B_4_fH;is#\!JG$&_)+`Brnc&mA3AVbI]%Ee?EZ8jR1DQl)`g:X\!/:0:At:W=]W? +AnN)g\nfaIY,.4,IEA@h53R#NC!n!ME)#'5oCUX\`YVV2iVXt,Uq3g>a#-ACR4GAD6`s*D01DXTHWl.& +WU[&eW0""M/P!Wn[=b/dUPuX`2>#iOD17r7.)kj$cmj)2#3c7'WFT]a_f]VJ"QumY[^*9)&tNXARZ:O@ +>uj6,[6iCm=gjB)[HPWt:!(J>XJ`J;('^lK)3^q]EAsT`l9)a6D\RU#L+!A*4_k[e1$Yc?C;o(paU+b6 +Gbba:c8YW)V:R%ojCO>ooo`e_jZ7&1)MR\q8Rp+md>\5o=2L60l*"<`-lFq&1m\pJ+6R?@jRG6-0KcJ[ +jc4gS-O/Wgijp-Mi,qd* +&<>k!d^)[E4?A?U=`:-3b?kGtSD>E;>OPPp@cMTGR&-`&+gAl3<7O(iW+#0RT$D[U?8*So<8&&"$C[BY +agCjj[fRW0ZkaF/m'N\a>/c8fW:LAF8W5OSBep"4Y\4G":O%@pK*8`pUaWu%"6T3N8RKWL6_XFDZ.qJ+Nluc6IYM=Jpa2md.c"^;A)\l+Uc>(?r +%hQ2??m.EqcTUu6q<#FG!s1Q]1$nZ#`^(IGRZ3rtl1[s_k=r3bI"!)r=jsa6F(L(3cg7/pq$^3n=d;E@ +:!5K`AGo!cd*-H<>Gn:0$._&fX]&Jc(\07Z3GSRD5&Ze)3=6O9F.iqG#.VpVN7N(F'!Wb>k!T#Ff69>QbQ.>?5Ch3AsK +olI1]I^K*S'8S+bKBjN&2Q$YNhqE)@h5&b+) ++Y$;)K(bhmGDU^#+F7K[=;jRA_s'XVCLL=?[h*p7"hb^@JpXele8oLKR.bq)NkG,Pe*a<+Q[Ec=nppN^ +n]&gT)[AQX`r[S0JEA."E;,R<(*'siCGn`3>MJ]L?](1ti6>?Vjhi-&jhjP7+#^!Dn6\dOn@b09^fSRB +&'QQXnfc+LZK20iX13OJ1>#;&nD:B$&"\.77m:%hbFoB_Rhg,%k'-1dJfsu?h!HSTIW,mI=KVpfORMZ& +pGUd4]Y4?b8)& +endstream +endobj +7 0 obj + 28923 +endobj +3 0 obj + << + /Parent null + /Type /Pages + /MediaBox [0.0000 0.0000 431.00 434.00] + /Resources 8 0 R + /Kids [5 0 R] + /Count 1 + >> +endobj +9 0 obj + [/PDF /Text /ImageC] +endobj +10 0 obj + << + /S /Transparency + /CS /DeviceRGB + /I true + /K false + >> +endobj +11 0 obj + << + /Alpha1 + << + /ca 1.0000 + /CA 1.0000 + /BM /Normal + /AIS false + >> + >> +endobj +8 0 obj + << + /ProcSet 9 0 R + /ExtGState 11 0 R + >> +endobj +xref +0 12 +0000000000 65535 f +0000000015 00000 n +0000000315 00000 n +0000029666 00000 n +0000000445 00000 n +0000000521 00000 n +0000000609 00000 n +0000029642 00000 n +0000030120 00000 n +0000029836 00000 n +0000029875 00000 n +0000029977 00000 n +trailer +<< + /Size 12 + /Root 2 0 R + /Info 1 0 R +>> +startxref +30193 +%%EOF diff -Nru ausweisapp2-2.3.1/docs/installation_en/README.en.rst ausweisapp2-2.4.0/docs/installation_en/README.en.rst --- ausweisapp2-2.3.1/docs/installation_en/README.en.rst 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_en/README.en.rst 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,450 @@ +Installation +~~~~~~~~~~~~ + +Windows +------- + +Start the installer of |AppName| using the command line to configure the +installation process and preset system-wide default settings. +The return value of msiexec indicates the result of the installation [#msiexecreturnvalues]_. +In addition to the usual arguments [#standardarguments]_, the following command +contains all supported arguments, which are explained below. + +.. code-block:: winbatch + + msiexec /i AusweisApp-X.YY.Z.msi /quiet INSTALLDIR="C:\AusweisApp" SYSTEMSETTINGS=false DESKTOPSHORTCUT=false PROXYSERVICE=false AUTOSTART=false TRAYICON=true AUTOHIDE=false REMINDTOCLOSE=false ASSISTANT=false TRANSPORTPINREMINDER=false CUSTOMPROXYTYPE="HTTP" CUSTOMPROXYHOST="proxy.example.org" CUSTOMPROXYPORT=1337 UPDATECHECK=false SHUFFLESCREENKEYBOARD=true SECURESCREENKEYBOARD=true ENABLECANALLOWED=true SKIPRIGHTSONCANALLOWED=true LAUNCH=true + +INSTALLDIR + States the installation directory. If not specified, the folder + "C:\\Program Files\\AusweisApp" is used. + +SYSTEMSETTINGS + Concerns the settings of firewall rules of the Windows Firewall. When not + specifying the argument, firewall rules are created (true). By indicating + SYSTEMSETTINGS=false, no firewall rules are created. + +DESKTOPSHORTCUT + By specifying DESKTOPSHORTCUT=false, no desktop shortcut is created. Without + specifying the argument, the desktop shortcut is created for all users (true). + +PROXYSERVICE + The proxy service is required to enable the parallel operation of several + entities of |AppName|. The proxy service monitors port 24727 (defined in + BSI TR-03124-1) and forwards requests to the local |AppName| instances. + The Discovery messages (amendment to BSI TR-03112-6 - IFD Service - Chapter + 3) are not forwarded, so that SaC devices cannot be recognized or used in + this operating mode. Not specified, the proxy service will be installed + automatically if Terminal Services is installed and the system is running + in application server mode. + +AUTOSTART + By setting AUTOSTART=true, an autostart entry is created for all users. + Users are unable to deactivate the autostart function in the |AppName|. Not + specified, no autostart entry is created (false). In that case, users are able + to activate the autostart function in the |AppName|. + +TRAYICON + Activates the tray icon to keep the |AppName| active in the background to always be available for + an authentication. + +AUTOHIDE + Concerns the automatic minimization after a successful authentication. Not + specified, it is activated (true). Setting AUTOHIDE=false, it is deactivated. + Users can adjust this setting to their preferences. + +REMINDTOCLOSE + Closing the |AppName| by clicking on the X, the user is notified that only the + user interface is closed and that the |AppName| is still available in the info + tray (if the tray icon is enabled) or that the |AppName| will be shut + down and the user needs to restart it to identify towards providers. + At this point, it is possible to prevent future notifications. Setting + REMINDTOCLOSE=false deactivates this notification from the outset. Not + specified, it is activated (true). + +ASSISTANT + Starting the |AppName| for the first time, the user interface is displayed and + the installation wizard is shown. With each subsequent start, the |AppName| + is started in the background, without the installation wizard being shown. By + indicating ASSISTANT=false, the |AppName| is started in the background without + the installation wizard from the outset. Not specified, the installation + wizard is activated (true). + +TRANSPORTPINREMINDER + Prior to the first authentication, the user is asked once whether they have + changed their Transport PIN. Setting TRANSPORTPINREMINDER=false deactivates this + reminder. Not specified, the reminder is activated (true). + +CUSTOMPROXYTYPE + Part of a proxy configuration. Valid values are SOCKS5 and HTTP. + All proxy parameters have to be set to use the proxy (see + CUSTOMPROXYHOST and CUSTOMPROXYPORT). You can disable the proxy after installation + with a checkbox in the settings. + +CUSTOMPROXYHOST + Part of a proxy configuration. Sets the Host of the proxy. All proxy parameters have + to be set to use the proxy (see CUSTOMPROXYTYPE and CUSTOMPROXYPORT). + You can disable the proxy after installation with a checkbox in the settings. + +CUSTOMPROXYPORT + Part of a proxy configuration. Sets the port of the proxy. Only values between + 1 and 65536 are valid. All proxy parameters have to be set to use the proxy (see + CUSTOMPROXYTYPE and CUSTOMPROXYHOST). You can disable the proxy after installation + with a checkbox in the settings. + +UPDATECHECK + Upon opening the user interface of the |AppName|, an update check is started, + provided that at least 24 hours have elapsed since the last update check. If a + newer version is available, the user is notified accordingly. Setting + UPDATECHECK to false or true deactivates or activates the update check + respectively. Users are unable to change this setting in the |AppName|. Not + specified, the update check is activated, but users can adjust the settings. + The UPDATECHECK parameter affects neither updates of the service + provider list nor updates of card reader information. + +SHUFFLESCREENKEYBOARD + If the on-screen keyboard is activated, the number keys can be arranged at random. + By setting SHUFFLESCREENKEYBOARD to false or true, the random arrangement can be + deactivated or activated. Users are able to adjust the setting. + +SECURESCREENKEYBOARD + If the on-screen keyboard is activated, the animation of the number keys can be + disabled. By setting SECURESCREENKEYBOARD to false or true, the animation can be + activated or deactivated. Users are able to adjust the setting. + +ENABLECANALLOWED + Enables support for the CAN allowed mode. If the provider got issued a corresponding authorization + certificate the ID card can be read by entering the CAN instead of the PIN. + +SKIPRIGHTSONCANALLOWED + Skips the page with the authorization certificate in the CAN allowed mode and asks directly for + the CAN. + +LAUNCH + Starts the |AppName| after the installation has finished. + +Alternatively, Orca [#orca]_ can be used to create an MST file that defines the +above arguments. The arguments are available in the "Directory" and "Property" +tables. The MST file can be transferred with the following command: + +.. code-block:: winbatch + + msiexec /i AusweisApp-X.YY.Z.msi /quiet TRANSFORMS=file.mst + +In order to optimize the start of the |AppName| on systems with no graphics +acceleration, the system variable "QT_QUICK_BACKEND" can be set to the value +"software". In this case, the |AppName| does not attempt to use graphics +acceleration and starts directly with the alternative software renderer. + +macOS +----- + +MacOS does not provide a command line installation. However, some of the above +settings can be specified system-wide by a plist file in the +/Library/Preferences directory. This plist file must be manually stored by the +administrator of the system and will be used by all (future) installations of +|AppName|. All not mentioned settings are not supported on macOS. The name of +the file must be "com.governikus.AusweisApp2.plist". The content is shown below: + +.. code-block:: xml + + + + + + trayIcon + + autoCloseWindow + + remindToClose + + showOnboarding + + transportPinReminder + + customProxyType + HTTP + customProxyHost + proxy.example.org + customProxyPort + 1337 + shuffleScreenKeyboard + + visualPrivacy + + enableCanAllowed + + skipRightsOnCanAllowed + + + + +The description for each value is applicable for both Windows and macOS, +although the naming of the attributes differs, as shown in the following table: + +======================== ======================= +macOS Windows +======================== ======================= +trayIcon TRAYICON +autoCloseWindow AUTOHIDE +remindToClose [#dialog]_ REMINDTOCLOSE +showOnboarding ASSISTANT +transportPinReminder TRANSPORTPINREMINDER +customProxyType CUSTOMPROXYTYPE +customProxyPort CUSTOMPROXYPORT +customProxyHost CUSTOMPROXYHOST +shuffleScreenKeyboard SHUFFLESCREENKEYBOARD +visualPrivacy SECURESCREENKEYBOARD +enableCanAllowed ENABLECANALLOWED +skipRightsOnCanAllowed SKIPRIGHTSONCANALLOWED +======================== ======================= + +It might be necessary to force a reload of the data cached by the operating +system: :code:`killall -u $USER cfprefsd` + +.. [#msiexecreturnvalues] https://docs.microsoft.com/en-us/windows/desktop/msi/error-codes +.. [#standardarguments] https://docs.microsoft.com/en-us/windows/desktop/msi/standard-installer-command-line-options +.. [#orca] https://docs.microsoft.com/en-us/windows/desktop/Msi/orca-exe +.. [#dialog] On macOS the |AppName| is minimized to the menu bar. + + +Operational Environment Requirements +------------------------------------ + +Required authorization for installation and execution +''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Administrator privileges are required to install the |AppName|. + +The execution of the |AppName| does not require administrator privileges. + +Used network ports +'''''''''''''''''' + +All network ports used by the |AppName| are listed in :numref:`porttable_en`. +:numref:`communicationmodel_en` shows a schematic representation of the +individual connections made by the |AppName|. + +The |AppName| starts a HTTP-Server on port 24727. +The server binds only to the localhost network interface. +The availability of the local server is necessary for the online eID function, +because providers will redirect the user with a HTTP redirect to the +local server to continue the authentication process in the |AppName| (eID1). +The server is also used to offer other local applications to use the +|AppName| via a websocket interface (SDK function, eID-SDK). +Therefore local incoming network connections to TCP Port 24727 must be +permitted. + +If the proxy service is activated, the |AppName| proxy takes over the server +functions of |AppName| on port 24727. The entities of |AppName| recognize +the proxy and use a free random port in this case to which the proxy forwards +the requests. + +Broadcast on UDP port 24727 in the local subnet have to be receivable by the +|AppName| to use the "Smartphone as Card Reader" functionality. +It may be necessary to deactivate AP isolation on your router. + +.. _communicationmodel_en: +.. figure:: CommunicationModel_en.pdf + + Communication model of the |AppName| + +The installer of the |AppName| provides an option to register all needed +firewall rules in the Windows Firewall. +If the rules are not registered, the user will be prompted by the Windows +Firewall to allow the outgoing connections once the |AppName| tries to +connect to a server. +These prompts are suppressed by registering the firewall rules during +installation. +No rules have to be added to the Windows Firewall for the local connections +eID1 and eID-SDK (when using the standard settings). + +In table :numref:`firewalltable_en` all firewall rules registered by the +installer are listed. + +TLS connections +''''''''''''''' + +Transmitted TLS certificates are solely validated via the interlacing with +the authorization certificate issued by the german eID PKI. +CA certificates in the Windows truststore are thus ignored. +It is therefore generally not possible to use the |AppName| behind a +TLS termination proxy. + +.. raw:: latex + + \begin{landscape} + +.. _porttable_en: +.. csv-table:: Network connections of the |AppName| + :header: "Reference", "Protocol", "Port", "Direction", "Optional", "Purpose", "Note" + :widths: 8, 8, 8, 8, 8, 35, 25 + + "eID1", TCP, 24727 [#aa2proxy]_, "incoming", "no", "Online eID function, eID activation [#TR-03124]_", "Only accessible from localhost [#TR-03124]_" + "eID2", TCP, 443 [#eidports]_, "outgoing", "no", "Online eID function, connection to the provider, TLS-1-2 channel [#TR-03124]_", "TLS certificates interlaced with authorization certificate [#TR-03124]_" + "eID3", TCP, 443 [#eidports]_, "outgoing", "no", "Online eID function, connection to eID-Server, TLS-2 channel [#TR-03124]_", "TLS certificates interlaced with authorization certificate [#TR-03124]_" + "eID-SDK", TCP, 24727 [#aa2proxy]_, "incoming", "no", "Usage of the SDK functionality", "Only accessible from localhost [#TR-03124]_" + "SaC1", UDP, 24727 [#aa2proxy]_, "incoming", "yes", "Smartphone as Card Reader, detection [#TR-03112]_", "Broadcasts" + "SaC2", TCP, , "outgoing", "yes", "Smartphone as Card Reader, usage [#TR-03112]_", "Connection in local subnet" + "Update", TCP, 443, "outgoing", "yes", "Updates [#govurl]_ of provider and card reader information as well as information on new |AppName| versions [#updatecheck]_ .", "TLS certificates will be validated against CA certificates included in the |AppName|. CA certificates provided by the OS are ignored." + +.. [#aa2proxy] Or a random port when using |AppName| proxy. +.. [#TR-03124] See TR-03124 specification from the BSI +.. [#eidports] Port 443 is used for the initial contact with the provider or + eID server. Due to configuration of the service on the service provider's + behalf, any other port might be used by forwarding. +.. [#TR-03112] See TR-03112-6 specifiaction from the BSI +.. [#govurl] All updates are based on the URL https://updates.autentapp.de/ +.. [#updatecheck] Automatic checks for new |AppName| versions can be deactivated, see commandline parameter + UPDATECHECK. + +.. _firewalltable_en: +.. csv-table:: Firewall rules of the |AppName| + :header: "Name", "Protocol", "Port", "Direction", "Connection reference" + :widths: 25, 15, 15, 15, 30 + :align: left + + "AusweisApp-Firewall-Rule", TCP, \*, "outgoing", "eID2, eID3, SaC2, Update" + "AusweisApp-SaC", UDP, 24727, "incoming", "SaC1" + +.. raw:: latex + + \end{landscape} + +Developer Options +~~~~~~~~~~~~~~~~~ + +|AppName| features so-called developer options. They provide advanced settings and +facilitate the integration of eID services. +The developer options are hidden by default. + +Activating the Developer Options +-------------------------------- + +Developer options are activated by clicking the "Application Version" accessible +via "Help" -> "Information" 10 times. After the 10th time, you will receive a +notification that the developer options are activated. Once activated, you will +find a new category "developer options" in the settings menu. In the mobile +versions additional options for "on-site reading" appear. + +Advanced Settings +----------------- + +The developer options offer advanced settings, which are explained below. + +Test mode for self-authentication (Test PKI) +'''''''''''''''''''''''''''''''''''''''''''' + +In general, the self-authentication is a built-in service of |AppName| and +can only be used with genuine ID cards. However, when in test mode, |AppName| +uses a test service allowing for self-authentication with a test ID card. +The test mode (Test PKI) of the self-authentication may be activated or +deactivated without using the Developer Options. By clicking on the magnifying +glass on the self-authentication start screen ten times, you can enable or +disable the test mode. + +Internal card Simulator +''''''''''''''''''''''' + +The internal card simulator allows to run an authentication in the Test PKI +without any ID card or card reader. Note that no other card reader can be used in +the stationary versions while the simulator is activated. + +A single static profile is stored in the current version, which cannot be changed +via the graphical user interface. Only the SDK allows to change the profile's data +using the SET_CARD command. Further information can be found at the documentation +of |AppName| SDK (see :ref:`Software Development Kit (SDK) `). + +Developer mode (stationary only) +'''''''''''''''''''''''''''''''' + +When the developer mode is activated, some safety measures during an +authentication process are ignored. Ignoring the safety measures with test +services usually employed in test scenarios, yields a successful authentication. +Each safety breach will be highlighted as an internal notification in |AppName| +or the operating system respectively. + +The following safety tests are disabled in the developer mode: + +* The used TLS keys and ephemeral TLS keys have the necessary minimum length. +* The URL of the TLS certificate description of the eID server and the TcToken URL + must fulfill the same-origin policy. +* The used TLS certificates must be entwined with the authorization certificate. +* The RefreshAddress URL and possible redirect URLs must conform to the HTTPS + scheme. + +**Please note:** +Developer mode can only be used for test services, usage with genuine provider +certificates is not possible. + +Support CAN Allowed mode for on-site reading (mobile only) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Enables support for the CAN allowed mode. If the provider got issued a +corresponding authorization certificate the ID card can be read by entering the +CAN instead of the PIN. + +Skip rights page +'''''''''''''''' + +Skips the page with the authorization certificate in the CAN allowed mode and asks +directly for the CAN. + +.. _SDK_En: + +Software Development Kit (SDK) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Possible Uses +------------- + +The software development kit (SDK) of |AppName| enables you to integrate the eID +function directly into your own application or app. This enables users to +authenticate themselves without media discontinuity. + +The SDK offers the advantage of being able to carry out an online +authentication in your own brand design - without users having to leave the +familiar environment. + +The |AppName| SDK also enables the integration of on-site reading. In this case, +the CAN is transmitted instead of the PIN to enable data transmission. You find +the CAN on the front of the ID card and you need it to enable the readout process. + +Integration Options +------------------- + +With the fully integrated version of the SDK, |AppName| is integrated into your +own application as an AAR package or Swift package. The advantage: |AppName| is +delivered directly with the application so that users don't have to install +|AppName| separately on their smartphone. + +With the partially integrated version of the SDK, |AppName| is called in the +background. Where applicable, however, the app can be delivered with the installer +regardless of partial integration. + +.. table:: Integration options for the different platforms + + +-----------------+----------------------+------------------+ + | | partially integrated | fully integrated | + +=================+======================+==================+ + | Windows / macOS | Yes | No | + +-----------------+----------------------+------------------+ + | Android | No | Yes | + +-----------------+----------------------+------------------+ + | iOS | No | Yes | + +-----------------+----------------------+------------------+ + +Developer documentation +----------------------- + +You can find a detailed developer documentation of the SDK with a list of possible +failure codes at https://www.ausweisapp.bund.de/sdk/. + +SDK Wrapper +----------- + +You can use the SDK Wrapper of the |AppName| to simplify the integration of +the SDK into your app. The SDK Wrapper offers Swift and Kotlin bindings for iOS +and Android. + +You can find information for integrating the SDK Wrapper in the developer +documentation at https://www.ausweisapp.bund.de/sdkwrapper/. diff -Nru ausweisapp2-2.3.1/docs/installation_en/conf.py.in ausweisapp2-2.4.0/docs/installation_en/conf.py.in --- ausweisapp2-2.3.1/docs/installation_en/conf.py.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_en/conf.py.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import sys +import os +import shlex + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +needs_sphinx = '1.4' + +# If true, figures, tables and code-blocks are automatically numbered +# if they has caption. For now, it works only with the HTML builder. +# Default is False. +numfig = True + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +locale_dirs = ['@SPHINX_DOCS_DIR@/locales/'] + +gettext_additional_targets = ['image'] +gettext_location = False +gettext_compact = True + +# Add any paths that contain templates here, relative to this directory. +#templates_path = ['@SPHINX_DOCS_DIR@/_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = '@PROJECT_NAME@ Extended documentation for administrators and developers' +copyright = '2018-2025, Governikus GmbH & Co. KG' +author = 'Governikus GmbH & Co. KG' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '@PROJECT_VERSION@' +# The full version, including alpha/beta/rc tags. +release = '@VERSION_DVCS@' + +today = ' ' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = '@SPHINX_LANG@' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +#exclude_patterns = [''] + + + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = '@SPHINX_DOCS_DIR@/../../resources/images/desktop/npa.ico' + +#html_theme_path = ['@SPHINX_DOCS_DIR@/_themes'] + +#html_theme = 'appcast' +html_theme = 'sphinx_rtd_theme' + +# If false, no module index is generated. +html_domain_indices = True + +# If false, no index is generated. +html_use_index = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +html_show_sphinx = False + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +html_show_copyright = True + +html_scaled_image_link = False + +# Output file base name for HTML help builder. +htmlhelp_basename = '@PROJECT_NAME@InstallationIntegration' + +html_context = { + 'display_github': False, + 'display_bitbucket': False, + 'show_source': False, + 'html_show_sourcelink': False, +} + +html_permalinks = True + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +'papersize': 'a4paper', + +# The font size ('10pt', '11pt' or '12pt'). +'pointsize': '11pt', + +# Additional stuff for the LaTeX preamble. +'preamble': ''' +\\usepackage{lscape} +\\hypersetup{pdfauthor={Governikus GmbH \& Co. KG}, + pdftitle={@PROJECT_NAME@}, + pdfsubject={Extended documentation for administrators and developers}, + pdfkeywords={installation, integration}, + pdfproducer={LaTeX}, + pdfcreator={Sphinx} +} +''', + +# Override tableofcontents +'tableofcontents': ''' +% Remove header of tableofcontent +\\makeatletter +\\@starttoc{toc} +\\makeatother + +\\newpage +\\pagestyle{plain} +\\pagenumbering{arabic} +''', + +# Latex figure (float) alignment +'figure_align': 'H', +'sphinxsetup' : 'HeaderFamily=\\linespread{1.9}\\sffamily\\bfseries', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, '@PROJECT_NAME@-@VERSION_DVCS@-NetInstallation_Integration_en.tex', '@PROJECT_NAME@ \\linebreak Extended documentation for administrators and developers', + 'Governikus GmbH \& Co. KG', 'howto'), +] + +# 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/npa.png' + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +latex_show_urls = 'footnote' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + +rst_epilog = """ +.. |AppName| replace:: @PROJECT_NAME@ +""" diff -Nru ausweisapp2-2.3.1/docs/installation_en/index.rst ausweisapp2-2.4.0/docs/installation_en/index.rst --- ausweisapp2-2.3.1/docs/installation_en/index.rst 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_en/index.rst 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,7 @@ +Table of contents +----------------- + +.. toctree:: + :maxdepth: 5 + + README.en diff -Nru ausweisapp2-2.3.1/docs/installation_en/metadata.yaml.in ausweisapp2-2.4.0/docs/installation_en/metadata.yaml.in --- ausweisapp2-2.3.1/docs/installation_en/metadata.yaml.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/installation_en/metadata.yaml.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,10 @@ +pdfauthor: @VENDOR@ +app_name: @PROJECT_NAME@ +pdfsubject: Extended documentation for administrators and developers +pdfkeywords: installation, integration +author: @VENDOR@ +documenttitle: @PROJECT_NAME@ Extended documentation for administrators and developers +subtitle: @VERSION_DVCS@ +language_iso639: en +language_babel: english +tocdepth: 3 diff -Nru ausweisapp2-2.3.1/docs/releasenotes/2.3.2.rst ausweisapp2-2.4.0/docs/releasenotes/2.3.2.rst --- ausweisapp2-2.3.1/docs/releasenotes/2.3.2.rst 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/2.3.2.rst 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,25 @@ +AusweisApp 2.3.2 +^^^^^^^^^^^^^^^^ + +**Releasedatum:** 26. Juni 2025 + + +Anwender +"""""""" +- Verbesserungen im Bereich der Barrierefreiheit. + +- Visuelle Anpassungen und Optimierungen der grafischen Oberfläche. + +- Das kurzzeitige Entfernen des Ausweises auf einem Android Smartphone als + Kartenleser vor der Passworteingabe führt nicht mehr zu einem Fehler. + +- Unter Windows werden jetzt auch Smartphones als Kartenleser erkannt, + die sich ausschließlich über IPv6 erreichen lassen. + +- Unterstützung von Smartphones als Kartenleser unter Windows Server. + + +Entwickler +"""""""""" +- Relative URL-Weiterleitungen bei einer Authentisierung werden korrekt + berücksichtigt. diff -Nru ausweisapp2-2.3.1/docs/releasenotes/2.4.0.rst ausweisapp2-2.4.0/docs/releasenotes/2.4.0.rst --- ausweisapp2-2.3.1/docs/releasenotes/2.4.0.rst 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/2.4.0.rst 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,23 @@ +AusweisApp 2.4.0 +^^^^^^^^^^^^^^^^ + +**Releasedatum:** 30. Oktober 2025 + + +Anwender +"""""""" +- Vollständige Herstellung der Barrierefreiheit. + +- Visuelle Anpassungen und Optimierungen der grafischen Oberfläche. + +- Beschleunigung der Verbindung mit einem Smartphone als Kartenleser. + +Entwickler +"""""""""" +- Aktualisierung von Qt auf die Version 6.9.2. + +- Aktualisierung von OpenSSL auf die Version 3.5.4. + +- Aktualisierung vom Android NDK auf r28c (28.2.13676358). + +- Absturz bei der Verwendung des SDKs unter Android mit der Architektur x86_64 behoben. diff -Nru ausweisapp2-2.3.1/docs/releasenotes/announce.rst ausweisapp2-2.4.0/docs/releasenotes/announce.rst --- ausweisapp2-2.3.1/docs/releasenotes/announce.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/announce.rst 2025-10-30 10:10:48.000000000 +0000 @@ -1,6 +1,13 @@ Abkündigungen ============= +Mit der Version 2.5.0 der AusweisApp wird die Unterstützung +folgender Systeme eingestellt. + +- macOS Ventura 13 +- iOS 16 + + Mit der Version 2.3.0 der AusweisApp wurde die Unterstützung folgender Systeme eingestellt. diff -Nru ausweisapp2-2.3.1/docs/releasenotes/appcast.rst ausweisapp2-2.4.0/docs/releasenotes/appcast.rst --- ausweisapp2-2.3.1/docs/releasenotes/appcast.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/appcast.rst 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -Release Notes -============= - -.. toctree:: - :maxdepth: 1 - - 2.3.1 - 2.3.0 - announce - issues diff -Nru ausweisapp2-2.3.1/docs/releasenotes/conf.py.in ausweisapp2-2.4.0/docs/releasenotes/conf.py.in --- ausweisapp2-2.3.1/docs/releasenotes/conf.py.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/conf.py.in 2025-10-30 10:10:48.000000000 +0000 @@ -65,7 +65,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['appcast.rst'] +exclude_patterns = [] @@ -80,7 +80,6 @@ html_theme_path = ['@SPHINX_DOCS_DIR@/_themes'] -#html_theme = 'appcast' html_theme = 'sphinx_rtd_theme' # If false, no module index is generated. diff -Nru ausweisapp2-2.3.1/docs/releasenotes/issues.rst ausweisapp2-2.4.0/docs/releasenotes/issues.rst --- ausweisapp2-2.3.1/docs/releasenotes/issues.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/issues.rst 2025-10-30 10:10:48.000000000 +0000 @@ -12,12 +12,6 @@ Kartenleser beendet wird, oder dieser den Prozess selbstständig wegen einer Zeitüberschreitung abbricht. -- 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. - - Vereinzelt kann es bei Problemen mit dem Grafikkartentreiber, insbesondere bei der Verwendung einer virtuellen Maschine, zu Darstellungsfehlern in der Anwendung kommen. Unter Umständen wird kein Fensterinhalt dargestellt. @@ -28,12 +22,6 @@ - Der Installationsdialog (bei Installation und auch Deinstallation) richtet sich nicht nach der Systemsprache. -- Auf macOS ist derzeit keine Unterstützung für die Funktion "Full Keyboard - Access" gegeben. - -- Die Verwendung relativer Redirect-URLs beim Abruf des TCTokens oder der - Weiterleitung zum Diensteanbieter führt zu einem Fehler in der |AppName|. - Android / iOS """"""""""""" @@ -43,19 +31,5 @@ - Unter Umständen kommt es zu Stabilitätsproblemen der NFC-Schnittstelle. -- Unter iOS ist derzeit keine Unterstützung für die Funktion "Full Keyboard - Access" gegeben. - - -Barrierearmut -""""""""""""" - -- Eine vollständige Barrierearmut ist zum aktuellen Zeitpunkt nicht - gegeben. Wir arbeiten kontinuierlich an einer Verbesserung. - -- Unter Android werden unsichtbare Inhalte vom Screenreader vorgelesen. - -- Die angeforderten Rechte während eines Ausweisvorgangs werden nicht optimal vom - Screenreader ausgegeben. - -- Der Screenreader kann teilweise Informationen an einigen Stellen doppelt ausgeben. +- Unter iOS 16 kommt es bei der Verwendung von VoiceOver zu einem + Absturz, wenn versucht wird ein Anwendungsprotokoll zu teilen. diff -Nru ausweisapp2-2.3.1/docs/releasenotes/metadata.yaml.in ausweisapp2-2.4.0/docs/releasenotes/metadata.yaml.in --- ausweisapp2-2.3.1/docs/releasenotes/metadata.yaml.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/metadata.yaml.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,10 @@ +pdfauthor: @VENDOR@ +app_name: @PROJECT_NAME@ +pdfsubject: Release Notes +pdfkeywords: release, notes +author: @VENDOR@ +documenttitle: @PROJECT_NAME@ Release Notes +subtitle: @VERSION_DVCS@ +language_iso639: de +language_babel: ngerman +tocdepth: 2 diff -Nru ausweisapp2-2.3.1/docs/releasenotes/singlehtml.conf.py.in ausweisapp2-2.4.0/docs/releasenotes/singlehtml.conf.py.in --- ausweisapp2-2.3.1/docs/releasenotes/singlehtml.conf.py.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/singlehtml.conf.py.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import sys -import os -import shlex - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.3' - -# If true, figures, tables and code-blocks are automatically numbered -# if they has caption. For now, it works only with the HTML builder. -# Default is False. -numfig = True - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [] - -locale_dirs = ['@SPHINX_DOCS_DIR@/locales/'] - -gettext_additional_targets = ['image'] -gettext_location = False -gettext_compact = True - -# Add any paths that contain templates here, relative to this directory. -#templates_path = ['@SPHINX_DOCS_DIR@/_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'appcast' - -# General information about the project. -project = '@PROJECT_NAME@' -copyright = '2016-2023, Governikus GmbH & Co. KG' -author = 'Governikus GmbH & Co. KG' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '@PROJECT_VERSION@' -# The full version, including alpha/beta/rc tags. -release = '@VERSION_DVCS@' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = '@SPHINX_LANG@' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['index.rst', 'support.rst', 'versions.rst', 'general.rst'] - - - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -html_favicon = '@SPHINX_DOCS_DIR@/../../resources/images/desktop/npa.ico' - -html_theme_path = ['@SPHINX_DOCS_DIR@/_themes'] - -html_theme = 'appcast' - -# If false, no module index is generated. -html_domain_indices = True - -# If false, no index is generated. -html_use_index = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -html_show_sphinx = False - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -html_show_copyright = True - -html_scaled_image_link = False - -# Output file base name for HTML help builder. -htmlhelp_basename = '@PROJECT_NAME@ReleaseNotes' - -html_context = { - 'display_github': False, - 'display_bitbucket': False, - 'show_source': False, - 'html_show_sourcelink': False, -} - -html_permalinks = False - -rst_epilog = """ -.. |AppName| replace:: @PROJECT_NAME@ -""" \ No newline at end of file diff -Nru ausweisapp2-2.3.1/docs/releasenotes/support.rst ausweisapp2-2.4.0/docs/releasenotes/support.rst --- ausweisapp2-2.3.1/docs/releasenotes/support.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/support.rst 2025-10-30 10:10:48.000000000 +0000 @@ -8,11 +8,13 @@ Betriebssysteme """"""""""""""" -- macOS 13.0 +- macOS 13 -- macOS 14.0 +- macOS 14 -- macOS 15.0 +- macOS 15 + +- macOS 26 - Windows 10 (64 Bit) ab Version 1809 @@ -30,7 +32,7 @@ - iOS 16 und höher -- ChromeOS 132 und höher +- ChromeOS 140 und höher Karten @@ -56,13 +58,13 @@ Im Rahmen der Qualitätssicherung werden die folgenden Browserversionen getestet. -- Chrome 133 +- Chrome 141 -- Firefox 135 +- Firefox 144 -- Safari 18.0 (macOS) +- Safari 26 (macOS) -- Edge 133 +- Edge 141 @@ -128,13 +130,15 @@ Anbieter umgesetzten Aktivierung. Daher empfehlen wir einen der folgenden Browser zu verwenden. -- Chrome 133 (iOS/Android) +- Chrome 141 (iOS/Android) + +- Firefox 144 (iOS/Android) -- Firefox 135 (iOS/Android) +- Edge 141 (iOS/Android) -- Samsung Internet 27 (Android) +- Samsung Internet 28 (Android) -- Safari 18.0 (iOS) +- Safari 26 (iOS) Kartenleser diff -Nru ausweisapp2-2.3.1/docs/releasenotes/text.conf.py.in ausweisapp2-2.4.0/docs/releasenotes/text.conf.py.in --- ausweisapp2-2.3.1/docs/releasenotes/text.conf.py.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/text.conf.py.in 2025-10-30 10:10:48.000000000 +0000 @@ -1,6 +1,6 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['appcast.rst', 'general.rst', 'support.rst', 'versions.rst', 'index.rst'] +exclude_patterns = ['general.rst', 'support.rst', 'versions.rst', 'index.rst'] # Sphinx needs a master document even though we just want to convert # some .rst files to .txt, so supply a document that is required anyway. diff -Nru ausweisapp2-2.3.1/docs/releasenotes/versions.rst ausweisapp2-2.4.0/docs/releasenotes/versions.rst --- ausweisapp2-2.3.1/docs/releasenotes/versions.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/releasenotes/versions.rst 2025-10-30 10:10:48.000000000 +0000 @@ -1,11 +1,20 @@ Versionen ========= +Versionszweig 2.4 +----------------- +.. toctree:: + :maxdepth: 1 + + 2.4.0 + + Versionszweig 2.3 ----------------- .. toctree:: :maxdepth: 1 + 2.3.2 2.3.1 2.3.0 diff -Nru ausweisapp2-2.3.1/docs/sdk/android.rst ausweisapp2-2.4.0/docs/sdk/android.rst --- ausweisapp2-2.3.1/docs/sdk/android.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/sdk/android.rst 2025-10-30 10:10:48.000000000 +0000 @@ -73,19 +73,6 @@ https://developer.android.com/studio/build/manifest-merge.html -App Bundle -^^^^^^^^^^ - -The |AppName| SDK uses native libraries which need to be extracted when -used in an App Bundle, otherwise the SDK will not work correctly. - -Add the following statement to your app's build.gradle file: - -.. code-block:: groovy - - android { packagingOptions { jniLibs { useLegacyPackaging = true } } } - - Logging ^^^^^^^ @@ -190,6 +177,8 @@ Callback """""""" +.. versionchanged:: 2.4.0 + Parameter **pIsSecureSessionId** from **sessionIdGenerated** removed. .. code-block:: java @@ -197,7 +186,7 @@ interface IAusweisApp2SdkCallback { - void sessionIdGenerated(String pSessionId, boolean pIsSecureSessionId); + void sessionIdGenerated(String pSessionId); void receive(String pJson); void sdkDisconnected(); } @@ -384,15 +373,8 @@ the first client. If you have successfully established a session, the **sessionIdGenerated** function -of your callback is invoked. With this invocation you receive two arguments. -**pIsSecureSessionId** is true if the SDK was able to gather enough entropy in -order to generate a secure random session ID. If it is false, there is no -session ID passed. There is nothing you can do about such an error. It results from -a problem with the random number generator, which in turn is very likely the result of -an ongoing local attack. The device should be considered manipulated and the user should be -informed. - -On success **pSessionId** holds the actual session ID generated by the SDK. +of your callback is invoked. With this invocation you receive one argument. +The **pSessionId** holds the actual session ID generated by the SDK. This ID is used to identify your session and you need to pass it to all future SDK function invocations of this session. @@ -415,8 +397,7 @@ public String mSessionID = null; @Override - public void sessionIdGenerated( - String pSessionId, boolean pIsSecureSessionId) throws RemoteException + public void sessionIdGenerated(String pSessionId) throws RemoteException { mSessionID = pSessionId; } diff -Nru ausweisapp2-2.3.1/docs/sdk/changelog.rst ausweisapp2-2.4.0/docs/sdk/changelog.rst --- ausweisapp2-2.3.1/docs/sdk/changelog.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/sdk/changelog.rst 2025-10-30 10:10:48.000000000 +0000 @@ -1,6 +1,14 @@ Changelog ========= +Version 2.4.0 +^^^^^^^^^^^^^ +* Updated Android NDK to r28c (28.2.13676358). +* Removed parameter **private** of the Simulator's :ref:`filesystem`. +* Removed parameter ``pIsSecureSessionId`` from ``sessionIdGenerated`` on Android. +* Gradle: useLegacyPackaging is no longer required and needs to be removed to support 16 KB page sizes on Android. + + Version 2.3.0 ^^^^^^^^^^^^^ * Removed support for iOS 14 and 15. diff -Nru ausweisapp2-2.3.1/docs/sdk/commands.rst ausweisapp2-2.4.0/docs/sdk/commands.rst --- ausweisapp2-2.3.1/docs/sdk/commands.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/sdk/commands.rst 2025-10-30 10:10:48.000000000 +0000 @@ -1,3 +1,5 @@ +.. _commands: + Commands -------- Your application (client) can send some commands (**cmd**) to diff -Nru ausweisapp2-2.3.1/docs/sdk/container.rst ausweisapp2-2.4.0/docs/sdk/container.rst --- ausweisapp2-2.3.1/docs/sdk/container.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/sdk/container.rst 2025-10-30 10:10:48.000000000 +0000 @@ -1,3 +1,5 @@ +.. _container: + Container ========= This chapter deals with the container specific properties of the |AppName| SDK. diff -Nru ausweisapp2-2.3.1/docs/sdk/desktop.rst ausweisapp2-2.4.0/docs/sdk/desktop.rst --- ausweisapp2-2.3.1/docs/sdk/desktop.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/sdk/desktop.rst 2025-10-30 10:10:48.000000000 +0000 @@ -1,3 +1,5 @@ +.. _desktop: + Desktop ======= This chapter deals with the desktop specific properties of the |AppName| SDK. @@ -111,7 +113,7 @@ .. note:: - It is possible to pass multiple plugins to the |AppName|, e.g.: ``--ui websocket --ui automatic``. + It is possible to pass multiple plugins to the |AppName|, e.g.: ``--ui webservice --ui automatic``. .. seealso:: The :doc:`container` SDK is designed for scripted and automatic workflows and enables diff -Nru ausweisapp2-2.3.1/docs/sdk/messages.rst ausweisapp2-2.4.0/docs/sdk/messages.rst --- ausweisapp2-2.3.1/docs/sdk/messages.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/sdk/messages.rst 2025-10-30 10:10:48.000000000 +0000 @@ -1,3 +1,5 @@ +.. _messages: + Messages -------- The |AppName| (server) will send some proper @@ -155,11 +157,14 @@ It lists all **available** API levels that can be used and set by :ref:`set_api_level`. Also it indicates the **current** selected API level. +.. versionadded:: 2.2.0 + Level **3** added. + .. versionadded:: 1.24.0 Level **2** added. - - **error**: Optional error message if :ref:`SET_API_LEVEL` failed. + - **error**: Optional error message if :ref:`set_api_level` failed. - **available**: List of supported API level by this version. @@ -170,8 +175,8 @@ { "msg": "API_LEVEL", "error": "optional error message like an invalid level", - "available": [1,2,3,4], - "current": 4 + "available": [1,2,3], + "current": 3 } Your application should always set the compatible API level. The |AppName| diff -Nru ausweisapp2-2.3.1/docs/sdk/metadata.yaml.in ausweisapp2-2.4.0/docs/sdk/metadata.yaml.in --- ausweisapp2-2.3.1/docs/sdk/metadata.yaml.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/docs/sdk/metadata.yaml.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,10 @@ +pdfauthor: @VENDOR@ +app_name: @PROJECT_NAME@ +pdfsubject: SDK +pdfkeywords: sdk, api +author: @VENDOR@ +documenttitle: @PROJECT_NAME@ SDK +subtitle: @VERSION_DVCS@ +language_iso639: en +language_babel: english +tocdepth: 2 diff -Nru ausweisapp2-2.3.1/docs/sdk/simulator.rst ausweisapp2-2.4.0/docs/sdk/simulator.rst --- ausweisapp2-2.3.1/docs/sdk/simulator.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/docs/sdk/simulator.rst 2025-10-30 10:10:48.000000000 +0000 @@ -1,8 +1,10 @@ +.. _simulator: + Simulator ========= The simulator provides a virtual card for testing purposes. It is enabled by default in all SDKs. This will be indicated by a :ref:`reader` -message with the parameter **name** being `Simulator`. +message with the parameter **name** being ``Simulator``. This virtual card can be inserted by :ref:`set_card` or can be automatically used in :ref:`automatic` mode. The default values of that virtual card are hardcoded and can be changed @@ -27,6 +29,9 @@ as **simulator** parameter in :ref:`set_card` or if you use the :ref:`automatic` mode of :doc:`desktop` or the :doc:`container` variant. +.. versionchanged:: 2.4.0 + Parameter **private** in **keys** removed. + .. versionadded:: 2.2.0 Parameter **content** in **keys** added. diff -Nru ausweisapp2-2.3.1/libs/CMakeLists.txt ausweisapp2-2.4.0/libs/CMakeLists.txt --- ausweisapp2-2.3.1/libs/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10.0) +cmake_minimum_required(VERSION 3.25) if(POLICY CMP0010) cmake_policy(SET CMP0010 NEW) @@ -12,11 +12,36 @@ cmake_policy(SET CMP0135 NEW) endif() -if(NOT ANDROID AND NOT IOS) - set(COMPILER_SEARCH C CXX) +project(AusweisApp_Libs) + +list(APPEND LIBS_ID_SALT ${CMAKE_VERSION}) +file(GLOB_RECURSE GENERATE_LIBS_ID_SEED "${CMAKE_SOURCE_DIR}/*") +list(FILTER GENERATE_LIBS_ID_SEED EXCLUDE REGEX "README|patches.cmake") + +if(CMAKE_TOOLCHAIN_FILE) + list(APPEND GENERATE_LIBS_ID_SEED "${CMAKE_TOOLCHAIN_FILE}") endif() -project(AusweisApp2_Libs ${COMPILER_SEARCH}) +function(GENERATE_LIBS_ID _out) + foreach(file ${GENERATE_LIBS_ID_SEED}) + file(MD5 "${file}" _hash) + string(MD5 hashes "${_hash}${hashes}${SALT}${LIBS_ID_SALT}") + endforeach() + set(${_out} ${hashes} PARENT_SCOPE) +endfunction() + +set(PROJECT_CMAKE_DIR "${PROJECT_SOURCE_DIR}/../cmake") +macro(use_project_include _file) + if(CONTAINER_SDK) + set(CMAKE_MODULE_PATH "${PROJECT_CMAKE_DIR};${PROJECT_SOURCE_DIR}") + else() + set(CMAKE_MODULE_PATH "${PROJECT_CMAKE_DIR}") + endif() + + include(${_file} RESULT_VARIABLE _file_path) + list(APPEND GENERATE_LIBS_ID_SEED ${_file_path}) + unset(CMAKE_MODULE_PATH) +endmacro() option(INTEGRATED_SDK "Build integrated specific SDK" OFF) option(CONTAINER_SDK "Build container specific SDK" OFF) @@ -24,14 +49,11 @@ if(CONTAINER_SDK) set(INTEGRATED_SDK ON) - set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../cmake;${PROJECT_SOURCE_DIR}") -else() - set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/../cmake") endif() include(ExternalProject) -include(Helper) -include(DVCS) +use_project_include(Helper) +use_project_include(DVCS) if(MSVC) @@ -53,11 +75,9 @@ message(FATAL_ERROR "Cannot find 'make' command") endif() -if(NOT DEFINED PROCESSOR_COUNT) - set(PROCESSOR_COUNT $ENV{PROCESSOR_COUNT}) -endif() - -if(NOT DEFINED PROCESSOR_COUNT) +if(DEFINED ENV{CMAKE_BUILD_PARALLEL_LEVEL}) + set(PROCESSOR_COUNT $ENV{CMAKE_BUILD_PARALLEL_LEVEL}) +else() include(ProcessorCount) ProcessorCount(PROCESSOR_COUNT) endif() @@ -83,14 +103,12 @@ set(PERL_EXECUTABLE perl) endif() -set(PATCH_CMAKE ${CMAKE_CURRENT_BINARY_DIR}/patch.cmake) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/patch.cmake.in ${PATCH_CMAKE} @ONLY) +set(PATCH_CMAKE ${PROJECT_SOURCE_DIR}/patch.cmake) if(NOT DESTINATION_DIR) set(DESTINATION_DIR ${PROJECT_BINARY_DIR}/dist) endif() -configure_file(${PROJECT_SOURCE_DIR}/test/valgrind.supp.${CMAKE_BUILD_TYPE} ${DESTINATION_DIR}/test/valgrind.supp COPYONLY) if(NOT PACKAGES_DIR) set(PACKAGES_DIR $ENV{PACKAGES_DIR}) @@ -102,7 +120,7 @@ string(REPLACE "\\" "/" PACKAGES_DIR ${PACKAGES_DIR}) set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/b) -include(Messages) +use_project_include(Messages) if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL CMAKE_SYSTEM_NAME) get_filename_component(compiler "${CMAKE_CXX_COMPILER}" NAME) @@ -111,6 +129,7 @@ ################################## Versions include(Versions.cmake) +list(APPEND LIBS_ID_SALT "${OPENSSL} ${QT}") ################################## Files set(QT_FILE qt-everywhere-src-${QT}.tar.xz) @@ -150,7 +169,13 @@ 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-sm2 no-sm3 no-sm4) set(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-ct no-dgram no-cast no-chacha no-blake2 no-rmd160 no-scrypt no-siphash no-whirlpool no-md4 no-des no-ec2m) 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 no-legacy) +set(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-deprecated no-engine no-async no-dso no-comp no-ts no-makedepend no-tests) +if(OPENSSL VERSION_GREATER "1.1.1") + set(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-legacy) +endif() +if(OPENSSL VERSION_GREATER_EQUAL "3.5") + set(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-tls-deprecated-ec) +endif() if(${CMAKE_BUILD_TYPE} STREQUAL "DEBUG") set(OPENSSL_CONFIGURE_FLAGS --debug ${OPENSSL_CONFIGURE_FLAGS}) @@ -178,7 +203,9 @@ set(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-asm no-shared) if(CMAKE_OSX_SYSROOT MATCHES "iphonesimulator") set(OPENSSL_ARCH iossimulator-xcrun) - if (CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") + if (CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64") + set(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} "-arch x86_64") + elseif (CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") set(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} "-arch arm64") endif() set(OPENSSL_COMPILER_FLAGS ${OPENSSL_COMPILER_FLAGS} -mios-simulator-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}) @@ -190,15 +217,21 @@ endif() set(OPENSSL_COMPILER_FLAGS ${OPENSSL_COMPILER_FLAGS} -fvisibility=hidden) elseif(APPLE) - if(NOT CMAKE_OSX_ARCHITECTURES AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") - set(CMAKE_OSX_ARCHITECTURES arm64) + if(NOT CMAKE_OSX_ARCHITECTURES) + if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") + set(CMAKE_OSX_ARCHITECTURES x86_64) + elseif(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64") + set(CMAKE_OSX_ARCHITECTURES arm64) + endif() endif() - if(CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") + if(CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64") + set(OPENSSL_ARCH darwin64-x86_64-cc) + elseif(CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") set(OPENSSL_CONFIGURE_FLAGS ${OPENSSL_CONFIGURE_FLAGS} no-asm) set(OPENSSL_ARCH darwin64-arm64-cc) else() - set(OPENSSL_ARCH darwin64-x86_64-cc) + message(FATAL_ERROR "Unknown CMAKE_OSX_ARCHITECTURES: ${CMAKE_OSX_ARCHITECTURES}") endif() set(OPENSSL_COMPILER_FLAGS ${OPENSSL_COMPILER_FLAGS} -mmacosx-version-min=13.0) @@ -232,14 +265,12 @@ elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64") set(OPENSSL_ARCH android-x86_64) set(OPENSSL_NDK_PREFIX x86_64) - ADD_FLAG(-Wl,-z,max-page-size=16384 NOQUOTES VAR OPENSSL_COMPILER_FLAGS USE_SAME_FOR_LINKER USE_LINKER_ONLY) elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86") set(OPENSSL_ARCH android-x86) set(OPENSSL_NDK_PREFIX i686) elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") set(OPENSSL_ARCH android-arm64) set(OPENSSL_NDK_PREFIX aarch64) - ADD_FLAG(-Wl,-z,max-page-size=16384 NOQUOTES VAR OPENSSL_COMPILER_FLAGS USE_SAME_FOR_LINKER USE_LINKER_ONLY) else() message(FATAL_ERROR "CMAKE_ANDROID_ARCH_ABI not supported by openssl: ${CMAKE_ANDROID_ARCH_ABI}") endif() @@ -278,12 +309,16 @@ set(OPENSSL_ENV ${CMAKE_COMMAND} -E env ${OPENSSL_ENV}) endif() +if(OPENSSL_PATCHES) + set(OPENSSL_PATCHES_CMD ${CMAKE_COMMAND} -DCOMPONENT=openssl -P ${PATCH_CMAKE}) +endif() + ExternalProject_Add(openssl URL ${OPENSSL_URLS} URL_HASH SHA256=${OPENSSL_HASH} DOWNLOAD_DIR ${PACKAGES_DIR} - PATCH_COMMAND ${CMAKE_COMMAND} -DCOMPONENT=openssl -P ${PATCH_CMAKE} + PATCH_COMMAND ${OPENSSL_PATCHES_CMD} CONFIGURE_COMMAND ${OPENSSL_ENV} ${PERL_EXECUTABLE} /Configure --prefix=${DESTINATION_DIR} --libdir=lib ${OPENSSL_CONFIGURE_FLAGS} ${OPENSSL_ARCH} "${OPENSSL_COMPILER_FLAGS}" BUILD_COMMAND ${OPENSSL_ENV} ${MAKE} ${MAKE_JOBS} INSTALL_COMMAND ${OPENSSL_ENV} ${MAKE} ${MAKE_JOBS} install_sw @@ -307,17 +342,9 @@ if(IOS) # Remove this work-around! Do not build any .dylib or be able to use .dylib # Globbing is not supported by cmake command mode! This will work if executed with unix shell only. - if(CMAKE_VERSION VERSION_LESS "3.17") - add_custom_command(TARGET openssl POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove ${DESTINATION_DIR}/lib/*.dylib) - else() - add_custom_command(TARGET openssl POST_BUILD COMMAND ${CMAKE_COMMAND} -E rm -f ${DESTINATION_DIR}/lib/*.dylib) - endif() + add_custom_command(TARGET openssl POST_BUILD COMMAND ${CMAKE_COMMAND} -E rm -f ${DESTINATION_DIR}/lib/*.dylib) elseif(ANDROID) - if(CMAKE_VERSION VERSION_LESS "3.17") - add_custom_command(TARGET openssl POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove ${DESTINATION_DIR}/lib/*.a) - else() - add_custom_command(TARGET openssl POST_BUILD COMMAND ${CMAKE_COMMAND} -E rm -f ${DESTINATION_DIR}/lib/*.a) - endif() + add_custom_command(TARGET openssl POST_BUILD COMMAND ${CMAKE_COMMAND} -E rm -f ${DESTINATION_DIR}/lib/*.a) endif() ################################## Qt @@ -347,6 +374,9 @@ set(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -prefix ${DESTINATION_DIR}) set(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -system-proxies -openssl-linked -I ${DESTINATION_DIR}/include -L ${DESTINATION_DIR}/lib) +version_to_hex(QT_HEX "${QT}") + +set(QT_CONFIGURE_FLAGS_SHARED ${QT_CONFIGURE_FLAGS_SHARED} -disable-deprecated-up-to ${QT_HEX}) set(QT_CONFIGURE_FLAGS_SHARED ${QT_CONFIGURE_FLAGS_SHARED} -opensource -confirm-license) set(QT_CONFIGURE_FLAGS_SHARED ${QT_CONFIGURE_FLAGS_SHARED} -nomake examples -nomake tests -no-mtdev -no-dbus) set(QT_CONFIGURE_FLAGS_SHARED ${QT_CONFIGURE_FLAGS_SHARED} -qt-zlib -qt-pcre -qt-harfbuzz) @@ -375,6 +405,8 @@ list(APPEND NO_FEATURES schannel sctp securetransport topleveldomain) # qtbase/src/testlib list(APPEND NO_FEATURES testlib_selfcover) +# qtconnectivity +list(APPEND NO_FEATURES bluetooth) # qtdeclarative/src/quickcontrols list(APPEND NO_FEATURES quickcontrols2-imagine quickcontrols2-material quickcontrols2-universal) if(NOT DEVELOPER) @@ -404,7 +436,7 @@ set(QT_MODULES qtbase,qtwebsockets,qtscxml) if(NOT INTEGRATED_SDK) - set(QT_MODULES ${QT_MODULES},qttranslations,qtdeclarative,qtimageformats,qttools,qtsvg,qtconnectivity,qtshadertools) + set(QT_MODULES ${QT_MODULES},qttranslations,qtdeclarative,qtimageformats,qttools,qtsvg,qtconnectivity) endif() set(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -submodules ${QT_MODULES}) @@ -426,7 +458,7 @@ if(CMAKE_OSX_SYSROOT MATCHES "iphonesimulator") set(QT_HOST_CMAKE_FLAGS ${QT_HOST_CMAKE_FLAGS} -DCMAKE_OSX_SYSROOT=macosx) - if (CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") + if(CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64" OR CMAKE_OSX_ARCHITECTURES STREQUAL "arm64") set(ADDITIONAL_QT_DEFINES ${ADDITIONAL_QT_DEFINES} -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}) endif() set(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -sdk iphonesimulator) @@ -460,24 +492,32 @@ set(ADDITIONAL_QT_DEFINES ${ADDITIONAL_QT_DEFINES} "-DCMAKE_CXX_FLAGS=\"-DQT_WIN_SERVER_2016_COMPAT=1\"") elseif(ANDROID) + list(APPEND LIBS_ID_SALT ${ANDROID_NDK_REVISION} ${ANDROID_BUILD_TOOLS_REVISION}) find_package(Java COMPONENTS Development REQUIRED) set(QT_ENV OPENSSL_LIBS=-lcrypto_${CMAKE_ANDROID_ARCH_ABI}\ -lssl_${CMAKE_ANDROID_ARCH_ABI}) set(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_OTHER} -android-sdk ${ANDROID_SDK_ROOT} -android-ndk ${CMAKE_ANDROID_NDK} -android-ndk-platform android-${CMAKE_SYSTEM_VERSION} -android-abis ${CMAKE_ANDROID_ARCH_ABI} -xplatform android-clang) - set(ANDROID_PAGE_SIZE_FLAGS "-Wl,-z,max-page-size=16384") set(ADDITIONAL_QT_DEFINES ${ADDITIONAL_QT_DEFINES} -DQT_ANDROID_API_USED_FOR_JAVA=android-${ANDROID_COMPILE_SDK_VERSION}) - if(CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a") - set(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} -no-use-gold-linker) - set(ADDITIONAL_QT_DEFINES ${ADDITIONAL_QT_DEFINES} -DCMAKE_SHARED_LINKER_FLAGS=${ANDROID_PAGE_SIZE_FLAGS} -DCMAKE_MODULE_LINKER_FLAGS=${ANDROID_PAGE_SIZE_FLAGS}) - elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64") - set(ADDITIONAL_QT_DEFINES ${ADDITIONAL_QT_DEFINES} -DCMAKE_SHARED_LINKER_FLAGS=${ANDROID_PAGE_SIZE_FLAGS} -DCMAKE_MODULE_LINKER_FLAGS=${ANDROID_PAGE_SIZE_FLAGS}) - endif() elseif(BSD) set(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_OTHER} -no-libudev) elseif(LINUX) - set(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_OTHER} -no-libproxy --linker=gold) + set(QT_CONFIGURE_FLAGS ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_OTHER} -no-libproxy) + find_program(LINKER ld.mold) + if(LINKER) + set(LINKER mold) + else() + find_program(LINKER ld.lld) + if(LINKER) + set(LINKER lld) + endif() + endif() + + if(LINKER) + message(STATUS "Use linker: ${LINKER}") + list(APPEND QT_CONFIGURE_FLAGS --linker=${LINKER}) + endif() else() message(FATAL_ERROR "Unsupported system") endif() @@ -499,6 +539,10 @@ endif() endif() +if(QT_PATCHES) + set(QT_PATCHES_CMD ${CMAKE_COMMAND} -DCOMPONENT=qt -P ${PATCH_CMAKE}) +endif() + # Build minimal qt for host tools until a dedicated target exists, see https://bugreports.qt.io/browse/QTQAINFRA-4203 if (BUILD_HOST_QT AND (IOS OR ANDROID)) list(APPEND ENABLED_TARGETS qt-host) @@ -508,15 +552,14 @@ set(QT_HOST_CONFIGURE_FLAGS -prefix ${QT_HOST_PATH} -release -optimize-size -shared -no-widgets -no-openssl -no-zstd -no-opengl) set(QT_HOST_CONFIGURE_FLAGS ${QT_HOST_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_SHARED}) set(QT_HOST_CMAKE_FLAGS ${QT_HOST_CMAKE_FLAGS} -DCMAKE_PREFIX_PATH=${QT_HOST_PATH}) - set(QT_HOST_CONFIGURE_FLAGS ${QT_HOST_CONFIGURE_FLAGS} -submodules qtbase,qtdeclarative,qtshadertools,qttools) + set(QT_HOST_CONFIGURE_FLAGS ${QT_HOST_CONFIGURE_FLAGS} -submodules qtbase,qtdeclarative,qttools) ExternalProject_Add(qt-host URL ${QT_URLS} URL_HASH SHA256=${QT_HASH} DOWNLOAD_DIR ${PACKAGES_DIR} - PATCH_COMMAND ${CMAKE_COMMAND} -DCOMPONENT=qt -P ${PATCH_CMAKE} && - ${CMAKE_COMMAND} -E touch qtbase/.gitignore + PATCH_COMMAND ${QT_PATCHES_CMD} CONFIGURE_COMMAND /${QT_CONFIGURE} ${QT_HOST_CONFIGURE_FLAGS} -- ${QT_HOST_CMAKE_FLAGS} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${MAKE_JOBS} INSTALL_COMMAND ${CMAKE_COMMAND} --install . @@ -533,14 +576,14 @@ URL_HASH SHA256=${QT_HASH} DOWNLOAD_DIR ${PACKAGES_DIR} - PATCH_COMMAND ${CMAKE_COMMAND} -DCOMPONENT=qt -P ${PATCH_CMAKE} && - ${CMAKE_COMMAND} -E touch qtbase/.gitignore + PATCH_COMMAND ${QT_PATCHES_CMD} CONFIGURE_COMMAND ${QT_ENV} /${QT_CONFIGURE} ${QT_CONFIGURE_FLAGS} ${QT_CONFIGURE_FLAGS_SHARED} -- -DOPENSSL_ROOT_DIR=${DESTINATION_DIR} -DCMAKE_PREFIX_PATH=${DESTINATION_DIR} ${ADDITIONAL_QT_DEFINES} BUILD_COMMAND ${CMAKE_COMMAND} --build . ${MAKE_JOBS} INSTALL_COMMAND ${CMAKE_COMMAND} --install . ) -add_custom_command(TARGET qt POST_BUILD COMMAND ${CMAKE_COMMAND} -E touch ${DESTINATION_DIR}/mkspecs/qt_vendor_governikus) +set(vendor_file "GovernikusConfig.cmake") +add_custom_command(TARGET qt POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/${vendor_file} ${DESTINATION_DIR}/lib/cmake/Governikus/${vendor_file}) ######################################################################### @@ -548,11 +591,7 @@ EXTERNALPROJECT_GET_PROPERTY(${var} INSTALL_DIR) list(APPEND CLEAN_TARGETS ${INSTALL_DIR}) endforeach() -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() +set_directory_properties(PROPERTIES ADDITIONAL_CLEAN_FILES "${DESTINATION_DIR};${CLEAN_TARGETS}") option(COMPRESS_DEPENDS "Disable DEPENDS for compress target" ON) if(COMPRESS_DEPENDS) @@ -560,55 +599,85 @@ endif() string(TIMESTAMP stamp "%Y-%m-%d") +set(IDENTIFIER ${stamp}) FIND_DVCS(${PROJECT_SOURCE_DIR}/..) if(DVCS_FOUND) GET_DVCS_INFO() + list(APPEND LIBS_ID_SALT ${dvcs_branch}) if(DEFINED dvcs_phase) - set(stamp ${stamp}_${dvcs_phase}) + set(IDENTIFIER ${IDENTIFIER}_${dvcs_phase}) endif() if(DEFINED dvcs_revision) - set(stamp ${stamp}_${dvcs_revision}) + set(IDENTIFIER ${IDENTIFIER}_${dvcs_revision}) endif() endif() +list(APPEND TARBALL_NAME Toolchain ${CMAKE_SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_PROCESSOR} ${CMAKE_CXX_COMPILER_ID} ${CMAKE_BUILD_TYPE}) 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}) + list(APPEND TARBALL_NAME ${CMAKE_ANDROID_ARCH_ABI}) +elseif(IOS) + if(CMAKE_OSX_SYSROOT MATCHES "iphonesimulator") + list(APPEND TARBALL_NAME iphonesimulator) + else() + list(APPEND TARBALL_NAME iphoneos) + endif() + if(CMAKE_OSX_ARCHITECTURES) + list(APPEND TARBALL_NAME ${CMAKE_OSX_ARCHITECTURES}) + endif() +elseif(LINUX) + set(OS_RELEASE "/etc/os-release") + if(EXISTS ${OS_RELEASE}) + file(STRINGS ${OS_RELEASE} _content) + function(EXTRACT _identifier _out) + foreach(line IN LISTS _content) + string(REGEX MATCH "^${_identifier}=\"?([a-zA-Z0-9.-_]+)\"?" _unused "${line}") + if(CMAKE_MATCH_1) + set(${_out} ${CMAKE_MATCH_1} PARENT_SCOPE) + break() + endif() + endforeach() + endfunction() + + EXTRACT(ID LINUX_DISTRO) + EXTRACT(VERSION_ID LINUX_DISTRO_VERSION) + list(APPEND TARBALL_NAME ${LINUX_DISTRO} ${LINUX_DISTRO_VERSION}) + endif() endif() if(WIN32) if(SIGNTOOL_CMD) - configure_file(${CMAKE_MODULE_PATH}/SignFiles.cmake.in ${CMAKE_BINARY_DIR}/SignFiles.cmake @ONLY) + configure_file(${PROJECT_CMAKE_DIR}/SignFiles.cmake.in ${CMAKE_BINARY_DIR}/SignFiles.cmake @ONLY) + list(APPEND GENERATE_LIBS_ID_SEED "${PROJECT_CMAKE_DIR}/SignFiles.cmake.in") set(SIGN_COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_BINARY_DIR}/SignFiles.cmake") endif() endif() -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() - -if(CMAKE_VERSION VERSION_LESS "3.17") - set(REMOVE_DIRECTORIES ${CMAKE_COMMAND} -E remove_directory "${DESTINATION_DIR}/doc" "${DESTINATION_DIR}/share") -else() - set(REMOVE_DIRECTORIES ${CMAKE_COMMAND} -E rm -rf "${DESTINATION_DIR}/doc" "${DESTINATION_DIR}/share") -endif() - -add_custom_target(compress.pre ${compressed_filename} - COMMAND ${REMOVE_DIRECTORIES} +add_custom_target(compress.pre + COMMAND ${CMAKE_COMMAND} -E rm -rf "${DESTINATION_DIR}/doc" "${DESTINATION_DIR}/share" COMMAND ${SIGN_COMMAND} DEPENDS ${COMPRESS_TARGETS} WORKING_DIRECTORY "${DESTINATION_DIR}") -set(compressed_filename Toolchain_${SYSTEM_NAME}_${stamp}.${COMPRESSION_FILENDING}) +if(SALT) + GENERATE_LIBS_ID(IDENTIFIER) +endif() +list(APPEND TARBALL_NAME ${IDENTIFIER}) +list(JOIN TARBALL_NAME "_" TARBALL_NAME) +set(compressed_filename ${TARBALL_NAME}.tar.zstd) +message(STATUS "Use tarball: ${compressed_filename}") add_custom_command(OUTPUT ${compressed_filename} - COMMAND ${CMAKE_COMMAND} -E tar "${COMPRESSION}" "${compressed_filename}" ${COMPRESSION_OPTION} "${DESTINATION_DIR}" + COMMAND ${CMAKE_COMMAND} -E tar cf "${compressed_filename}" --zstd "${DESTINATION_DIR}" DEPENDS compress.pre) add_custom_target(compress DEPENDS ${compressed_filename}) + +include(patch.cmake) +if(QT_PATCHES) + FETCH_PATCHES_NAMES(QT_PATCHES qt) +endif() +if(OPENSSL_PATCHES) + FETCH_PATCHES_NAMES(OPENSSL_PATCHES openssl) +endif() +include(CMakePackageConfigHelpers) +configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/${vendor_file}.in ${CMAKE_BINARY_DIR}/${vendor_file} INSTALL_DESTINATION ${DESTINATION_DIR}) diff -Nru ausweisapp2-2.3.1/libs/CMakePresets.json ausweisapp2-2.4.0/libs/CMakePresets.json --- ausweisapp2-2.3.1/libs/CMakePresets.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/CMakePresets.json 2025-10-30 10:10:48.000000000 +0000 @@ -50,17 +50,6 @@ } }, { - "name": "ci-gnu-debug", - "inherits": "ci", - "generator": "MinGW Makefiles", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "DEBUG", - "WIN_SIGN_KEYSTORE": "$env{WIN_SIGN_KEYSTORE}", - "WIN_SIGN_KEYSTORE_PSW": "$env{WIN_SIGN_KEYSTORE_PSW}", - "WIN_SIGN_SUBJECT_NAME": "$env{WIN_SIGN_SUBJECT_NAME}" - } - }, - { "name": "ci-android", "inherits": "ci", "toolchainFile": "${sourceParentDir}/cmake/android.toolchain.cmake", @@ -77,7 +66,7 @@ } }, { - "name": "ci-ios-simulator", + "name": "ci-ios-simulator-x86_64", "inherits": "ci-ios", "generator": "Xcode", "cacheVariables": { @@ -87,7 +76,7 @@ }, { "name": "ci-ios-simulator-arm64", - "inherits": "ci-ios-simulator", + "inherits": "ci-ios-simulator-x86_64", "cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "arm64" } diff -Nru ausweisapp2-2.3.1/libs/GovernikusConfig.cmake.in ausweisapp2-2.4.0/libs/GovernikusConfig.cmake.in --- ausweisapp2-2.3.1/libs/GovernikusConfig.cmake.in 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/GovernikusConfig.cmake.in 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,16 @@ +@PACKAGE_INIT@ + +set(LIBS_GOVERNIKUS ON) +set(LIBS_SYSTEM @CMAKE_SYSTEM_NAME@) +set(LIBS_BUILD_DATE "@stamp@") +set(LIBS_REVISION "@dvcs_revision@") +set(LIBS_BRANCH "@dvcs_branch@") +set(LIBS_PHASE "@dvcs_phase@") +set(LIBS_QT "@QT@") +set(LIBS_QT_PATCHES @QT_PATCHES@) +set(LIBS_OPENSSL "@OPENSSL@") +set(LIBS_OPENSSL_PATCHES @OPENSSL_PATCHES@) +set(LIBS_TARBALL "@compressed_filename@") +set(LIBS_IDENTIFIER "@IDENTIFIER@") + +check_required_components(Governikus) diff -Nru ausweisapp2-2.3.1/libs/README.rst ausweisapp2-2.4.0/libs/README.rst --- ausweisapp2-2.3.1/libs/README.rst 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/README.rst 2025-10-30 10:10:48.000000000 +0000 @@ -30,7 +30,7 @@ Notwendige Tools: -- CMake >= 3.19.0 +- CMake >= 3.25.0 - http://www.cmake.org diff -Nru ausweisapp2-2.3.1/libs/Versions.cmake ausweisapp2-2.4.0/libs/Versions.cmake --- ausweisapp2-2.3.1/libs/Versions.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/Versions.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,5 +1,28 @@ -set(QT 6.8.1) -set(QT_HASH 45e3a9f6d33c92ffe65a1fde1a8eba5b228112df675f7f9026eaa332b2e2edff) +######################################## Qt +if(NOT DEFINED QT) + set(QT 6.9.2) + set(QT_PATCHES ON) +endif() -set(OPENSSL 3.4.1) -set(OPENSSL_HASH 002a2d6b30b58bf4bea46c43bdd96365aaf8daa6c428782aa4feee06da197df3) +if(NOT QT_HASH) + set(QT_HASH_6.9.2 643f1fe35a739e2bf5e1a092cfe83dbee61ff6683684e957351c599767ca279c) + + set(QT_HASH ${QT_HASH_${QT}}) +endif() + + + +######################################## OpenSSL +if(NOT DEFINED OPENSSL) + set(OPENSSL 3.5.4) + set(OPENSSL_PATCHES ON) +endif() + +if(NOT OPENSSL_HASH) + set(OPENSSL_HASH_1.1.1w cf3098950cb4d853ad95c0841f1f9c6d3dc102dccfcacd521d93925208b76ac8) + set(OPENSSL_HASH_3.0.18 d80c34f5cf902dccf1f1b5df5ebb86d0392e37049e5d73df1b3abae72e4ffe8b) + set(OPENSSL_HASH_3.4.1 002a2d6b30b58bf4bea46c43bdd96365aaf8daa6c428782aa4feee06da197df3) + set(OPENSSL_HASH_3.5.4 967311f84955316969bdb1d8d4b983718ef42338639c621ec4c34fddef355e99) + + set(OPENSSL_HASH ${OPENSSL_HASH_${OPENSSL}}) +endif() diff -Nru ausweisapp2-2.3.1/libs/patch.cmake ausweisapp2-2.4.0/libs/patch.cmake --- ausweisapp2-2.3.1/libs/patch.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patch.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,68 @@ +cmake_minimum_required(VERSION 3.25) + +############################################# +# Usage: cmake -DCOMPONENT=XYZ -P patch.cmake +############################################# + +get_filename_component(CMAKE_SOURCE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY) + +function(PATCH_SOURCES_EXECUTE _patchlist) + foreach(_patch ${_patchlist}) + get_filename_component(filename "${_patch}" NAME) + message(STATUS "Apply patch: ${filename}") + execute_process(COMMAND "${PATCH_CMD}" ${PATCH_OPTIONS} ${_patch} RESULT_VARIABLE _result) + + if(NOT _result EQUAL 0) + message(FATAL_ERROR "Patch failed with exit code: ${_result}") + endif() + endforeach() +endfunction() + +function(FETCH_PATCHES _out _component) + set(PATCHES_DIR ${CMAKE_SOURCE_DIR}/patches) + file(GLOB PATCHES "${PATCHES_DIR}/${_component}*.patch") + set(${_out} ${PATCHES} PARENT_SCOPE) +endfunction() + +function(FETCH_PATCHES_NAMES _out _component) + FETCH_PATCHES(tmplist ${_component}) + foreach(_patch ${tmplist}) + get_filename_component(filename "${_patch}" NAME) + list(APPEND PATCHES ${filename}) + endforeach() + set(${_out} ${PATCHES} PARENT_SCOPE) +endfunction() + +function(PATCH_SOURCES _component) + FETCH_PATCHES(PATCHES ${_component}) + PATCH_SOURCES_EXECUTE("${PATCHES}") +endfunction() + +macro(SEARCH_PATCH_CMD) + if(NOT DEFINED ENV{FORCE_PATCH_PY}) + find_program(PATCH_CMD patch CMAKE_FIND_ROOT_PATH_BOTH) + endif() + + if(PATCH_CMD) + set(PATCH_OPTIONS -p1 -i) + if(WIN32) + set(PATCH_OPTIONS --verbose ${PATCH_OPTIONS}) + endif() + message(STATUS "Using 'patch' command... ${PATCH_CMD}") + else() + message(STATUS "Cannot find 'patch' command... using patch.py") + find_package(Python REQUIRED) + set(PATCH_CMD ${Python_EXECUTABLE}) + set(PATCH_OPTIONS ${CMAKE_SOURCE_DIR}/patch.py -p1 --debug -v) + endif() +endmacro() + + +if(CMAKE_SCRIPT_MODE_FILE) + if(COMPONENT MATCHES "qt|openssl") + SEARCH_PATCH_CMD() + PATCH_SOURCES(${COMPONENT}) + else() + message(FATAL_ERROR "Component unknown: ${COMPONENT}") + endif() +endif() diff -Nru ausweisapp2-2.3.1/libs/patch.cmake.in ausweisapp2-2.4.0/libs/patch.cmake.in --- ausweisapp2-2.3.1/libs/patch.cmake.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patch.cmake.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -cmake_minimum_required(VERSION 3.10.0) - -function(PATCH_SOURCES_EXECUTE _patchlist) - foreach(_patch ${_patchlist}) - get_filename_component(filename "${_patch}" NAME) - message(STATUS "Apply patch: ${filename}") - execute_process(COMMAND "${PATCH_CMD}" ${PATCH_OPTIONS} ${_patch} RESULT_VARIABLE _result) - - if(NOT _result EQUAL 0) - message(FATAL_ERROR "Patch failed with exit code: ${_result}") - endif() - endforeach() -endfunction() - -function(PATCH_SOURCES _component) - set(PATCHES_DIR @PROJECT_SOURCE_DIR@/patches) - - file(GLOB PATCHES "${PATCHES_DIR}/${_component}*.patch") - PATCH_SOURCES_EXECUTE("${PATCHES}") - - if("@CMAKE_BUILD_TYPE@" STREQUAL "DEBUG") - file(GLOB PATCHES_DEBUG "${PATCHES_DIR}/debug/${_component}*.patch") - PATCH_SOURCES_EXECUTE("${PATCHES_DEBUG}") - endif() -endfunction() - -macro(SEARCH_PATCH_CMD) - if(NOT DEFINED ENV{FORCE_PATCH_PY}) - find_program(PATCH_CMD patch CMAKE_FIND_ROOT_PATH_BOTH) - endif() - - if(PATCH_CMD) - set(PATCH_OPTIONS -p1 -i) - if(WIN32) - set(PATCH_OPTIONS --verbose ${PATCH_OPTIONS}) - endif() - message(STATUS "Using 'patch' command... ${PATCH_CMD}") - else() - message(STATUS "Cannot find 'patch' command... using patch.py") - find_package(Python REQUIRED) - set(PATCH_CMD ${Python_EXECUTABLE}) - set(PATCH_OPTIONS @PROJECT_SOURCE_DIR@/patch.py -p1 --debug -v) - endif() -endmacro() - - - -if(COMPONENT MATCHES "qt|openssl") - SEARCH_PATCH_CMD() - PATCH_SOURCES(${COMPONENT}) -else() - message(FATAL_ERROR "Component unknown: ${COMPONENT}") -endif() diff -Nru ausweisapp2-2.3.1/libs/patch.py ausweisapp2-2.4.0/libs/patch.py --- ausweisapp2-2.3.1/libs/patch.py 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patch.py 2025-10-30 10:10:48.000000000 +0000 @@ -214,7 +214,7 @@ def fromurl(url): """ Parse patch from an URL, return False - if an error occured. Note that this also + if an error occurred. Note that this also can throw urlopen() exceptions. """ ps = PatchSet( urllib_request.urlopen(url) ) diff -Nru ausweisapp2-2.3.1/libs/patches/openssl-0001-android-shlib_variant.patch ausweisapp2-2.4.0/libs/patches/openssl-0001-android-shlib_variant.patch --- ausweisapp2-2.3.1/libs/patches/openssl-0001-android-shlib_variant.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/openssl-0001-android-shlib_variant.patch 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -From 70de411d7b8cbd1c49c8941985f1e3fa66b37813 Mon Sep 17 00:00:00 2001 +From 7543830c26c20c8af299c99ee78671a960146c51 Mon Sep 17 00:00:00 2001 From: Lars Schmertmann Date: Tue, 19 Jan 2021 17:07:51 +0100 Subject: android shlib_variant diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0001-Android-Fix-logging-of-the-category.patch ausweisapp2-2.4.0/libs/patches/qtbase-0001-Android-Fix-logging-of-the-category.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0001-Android-Fix-logging-of-the-category.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0001-Android-Fix-logging-of-the-category.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,136 +0,0 @@ -From c146065aee6542c2bb9772c85387a8751215a5e8 Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Wed, 6 Nov 2024 12:29:50 +0100 -Subject: Android: Fix logging of the category - -87d8ee755bfdef8e72a122789c2e3ed382881a12 changed the logging behavior -on Android to use the category as tag. But it was missed that -`formatLogMessage` only fills the fields given in the messagePattern. -Our default messagePattern includes the category but on Android it -is never filled since this change. - -So we change the default messagePattern on Android to omit the category -and only use the category as tag when the category is not given in the -messagePattern. - -Pick-to: 6.8 6.5 -Task-number: QTBUG-94708 -Change-Id: I80f65d0f7f8c0ca9c2fff2dcd63d4599848b6e2b -Reviewed-by: Assam Boudjelthia -(cherry picked from commit 709bc29a1d2abf40bf3f1c067fe3b96b6e0c09d0) ---- - src/corelib/doc/src/external-resources.qdoc | 10 ++++++ - src/corelib/global/qlogging.cpp | 35 ++++++++++++++----- - .../corelib/global/qlogging/tst_qlogging.cpp | 4 --- - 3 files changed, 37 insertions(+), 12 deletions(-) - -diff --git x/qtbase/src/corelib/doc/src/external-resources.qdoc y/qtbase/src/corelib/doc/src/external-resources.qdoc -index 7c3653aa71137d0f6df388a018126510d879d9e0..c8d79d163e233ee21d0bad8c383f4b11b8331a6c 100644 ---- x/qtbase/src/corelib/doc/src/external-resources.qdoc -+++ y/qtbase/src/corelib/doc/src/external-resources.qdoc -@@ -181,3 +181,13 @@ - \externalpage https://developer.android.com/develop/connectivity/bluetooth/bt-permissions - \title Android Bluetooth Permissions - */ -+ -+/*! -+ \externalpage https://developer.android.com/reference/android/util/Log -+ \title Android: Log -+*/ -+ -+/*! -+ \externalpage https://developer.android.com/ndk/reference/group/logging#__android_log_print -+ \title Android: log_print -+*/ -diff --git x/qtbase/src/corelib/global/qlogging.cpp y/qtbase/src/corelib/global/qlogging.cpp -index 8bdf7c7b6ff25fee4212bff733adf5fc876f10cf..dff15a0f151f29d509891d5bf65e9f7831ccec62 100644 ---- x/qtbase/src/corelib/global/qlogging.cpp -+++ y/qtbase/src/corelib/global/qlogging.cpp -@@ -1130,7 +1130,11 @@ static const char ifFatalTokenC[] = "%{if-fatal}"; - static const char endifTokenC[] = "%{endif}"; - static const char emptyTokenC[] = ""; - -+#ifdef Q_OS_ANDROID -+static const char defaultPattern[] = "%{message}"; -+#else - static const char defaultPattern[] = "%{if-category}%{category}: %{endif}%{message}"; -+#endif - - struct QMessagePattern - { -@@ -1158,6 +1162,18 @@ struct QMessagePattern - - bool fromEnvironment; - static QBasicMutex mutex; -+ -+#ifdef Q_OS_ANDROID -+ bool containsToken(const char *token) const -+ { -+ for (int i = 0; tokens[i]; ++i) { -+ if (tokens[i] == token) -+ return true; -+ } -+ -+ return false; -+ } -+#endif - }; - #ifdef QLOGGING_HAVE_BACKTRACE - Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_RELOCATABLE_TYPE); -@@ -1619,10 +1635,7 @@ static QString formatLogMessage(QtMsgType type, const QMessageLogContext &contex - } else if (token == messageTokenC) { - message.append(str); - } else if (token == categoryTokenC) { --#ifndef Q_OS_ANDROID -- // Don't add the category to the message on Android - message.append(QLatin1StringView(context.category)); --#endif - } else if (token == typeTokenC) { - switch (type) { - case QtDebugMsg: message.append("debug"_L1); break; -@@ -1878,11 +1891,12 @@ static bool android_default_message_handler(QtMsgType type, - break; - }; - -- // If application name is a tag ensure it has no spaces -- // If a category is defined, use it as an Android logging tag -- __android_log_print(priority, isDefaultCategory(context.category) ? -- qPrintable(QCoreApplication::applicationName().replace(u' ', u'_')) : context.category, -- "%s\n", qPrintable(formattedMessage)); -+ QMessagePattern *pattern = qMessagePattern(); -+ const QString tag = (pattern && pattern->containsToken(categoryTokenC)) -+ // If application name is a tag ensure it has no spaces -+ ? QCoreApplication::applicationName().replace(u' ', u'_') -+ : QString::fromUtf8(context.category); -+ __android_log_print(priority, qPrintable(tag), "%s\n", qPrintable(formattedMessage)); - - return true; // Prevent further output to stderr - } -@@ -2299,6 +2313,11 @@ void qErrnoWarning(int code, const char *msg, ...) - - The default \a pattern is \c{%{if-category}%{category}: %{endif}%{message}}. - -+ \note On Android, the default \a pattern is \c{%{message}} because the category is used as -+ \l{Android: log_print}{tag} since Android logcat has has a dedicated field for the logging -+ categories, see \l{Android: Log}{Android Logging}. If a custom \a pattern including the -+ category is used, QCoreApplication::applicationName() is used as \l{Android: log_print}{tag}. -+ - The \a pattern can also be changed at runtime by setting the QT_MESSAGE_PATTERN - environment variable; if both \l qSetMessagePattern() is called and QT_MESSAGE_PATTERN is - set, the environment variable takes precedence. -diff --git x/qtbase/tests/auto/corelib/global/qlogging/tst_qlogging.cpp y/qtbase/tests/auto/corelib/global/qlogging/tst_qlogging.cpp -index defe3ac4210dc9cc2e8b979d87661fd7e5b2214d..c8cd60dcdaa985808a2fa283b7510eb300213741 100644 ---- x/qtbase/tests/auto/corelib/global/qlogging/tst_qlogging.cpp -+++ y/qtbase/tests/auto/corelib/global/qlogging/tst_qlogging.cpp -@@ -955,11 +955,7 @@ void tst_qmessagehandler::formatLogMessage_data() - << format << "[F] msg" - << QtFatalMsg << BA("") << 0 << BA("func") << QByteArray() << "msg"; - QTest::newRow("if_cat") --#ifndef Q_OS_ANDROID - << format << "[F] cat: msg" --#else -- << format << "[F] : msg" --#endif - << QtFatalMsg << BA("") << 0 << BA("func") << BA("cat") << "msg"; - } - diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0001-Do-not-override-OPENSSL_API_COMPAT.patch ausweisapp2-2.4.0/libs/patches/qtbase-0001-Do-not-override-OPENSSL_API_COMPAT.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0001-Do-not-override-OPENSSL_API_COMPAT.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0001-Do-not-override-OPENSSL_API_COMPAT.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,25 @@ +From eb678f100299f930f9f64e2055e051ccf4a4b128 Mon Sep 17 00:00:00 2001 +From: Klitzing +Date: Thu, 31 Aug 2023 13:19:55 +0200 +Subject: Do not override OPENSSL_API_COMPAT + +See QTBUG-83733 and AUTENTAPP-24481 + +Change-Id: Ied55e3d6ebd90fbbecb8c4d8d1638b1de3ba6969 +--- + src/plugins/tls/openssl/CMakeLists.txt | 2 -- + 1 file changed, 2 deletions(-) + +diff --git x/qtbase/src/plugins/tls/openssl/CMakeLists.txt y/qtbase/src/plugins/tls/openssl/CMakeLists.txt +index 0e0a7a1552dc6ef7e1b5847d5685e8b653806364..e176bbf9d5471ffc977d3a6344998f5c3274de5a 100644 +--- x/qtbase/src/plugins/tls/openssl/CMakeLists.txt ++++ y/qtbase/src/plugins/tls/openssl/CMakeLists.txt +@@ -20,8 +20,6 @@ qt_internal_add_plugin(QTlsBackendOpenSSLPlugin + LIBRARIES + Qt::NetworkPrivate + Qt::CorePrivate +- DEFINES +- OPENSSL_API_COMPAT=0x10100000L + ) + + if (WIN32) # Windows header issues diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0002-Do-not-override-OPENSSL_API_COMPAT.patch ausweisapp2-2.4.0/libs/patches/qtbase-0002-Do-not-override-OPENSSL_API_COMPAT.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0002-Do-not-override-OPENSSL_API_COMPAT.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0002-Do-not-override-OPENSSL_API_COMPAT.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -From 5c97da3b5d70571d6b822bbd181b0429a9bb8738 Mon Sep 17 00:00:00 2001 -From: Klitzing -Date: Thu, 31 Aug 2023 13:19:55 +0200 -Subject: Do not override OPENSSL_API_COMPAT - -See QTBUG-83733 and AUTENTAPP-24481 - -Change-Id: Ied55e3d6ebd90fbbecb8c4d8d1638b1de3ba6969 ---- - src/plugins/tls/openssl/CMakeLists.txt | 2 -- - 1 file changed, 2 deletions(-) - -diff --git x/qtbase/src/plugins/tls/openssl/CMakeLists.txt y/qtbase/src/plugins/tls/openssl/CMakeLists.txt -index 0e0a7a1552dc6ef7e1b5847d5685e8b653806364..e176bbf9d5471ffc977d3a6344998f5c3274de5a 100644 ---- x/qtbase/src/plugins/tls/openssl/CMakeLists.txt -+++ y/qtbase/src/plugins/tls/openssl/CMakeLists.txt -@@ -20,8 +20,6 @@ qt_internal_add_plugin(QTlsBackendOpenSSLPlugin - LIBRARIES - Qt::NetworkPrivate - Qt::CorePrivate -- DEFINES -- OPENSSL_API_COMPAT=0x10100000L - ) - - if (WIN32) # Windows header issues diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0002-Don-t-ignore-offscreen-A11y-elements-for-VoiceOver-o.patch ausweisapp2-2.4.0/libs/patches/qtbase-0002-Don-t-ignore-offscreen-A11y-elements-for-VoiceOver-o.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0002-Don-t-ignore-offscreen-A11y-elements-for-VoiceOver-o.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0002-Don-t-ignore-offscreen-A11y-elements-for-VoiceOver-o.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,26 @@ +From 6318d22bd374cff81f15bcc5dc9fae3b05219052 Mon Sep 17 00:00:00 2001 +From: Timon Sassor +Date: Tue, 29 Apr 2025 09:22:56 +0200 +Subject: Don't ignore offscreen A11y elements for VoiceOver on macOS + +As soon as an element moved offscreen (while still visible: true), VoiceOver couldn't focus it. So either the user had to constantly switch between scrolling and re-navigating to the content he wants to read or he might even miss additional available content. + +Fixes: QTBUG-135845 +Change-Id: Ie0b00002357c6efc21cf741680246d54d74f0c86 +--- + src/plugins/platforms/cocoa/qcocoaaccessibility.mm | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git x/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm y/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +index c825919e38edc75bbd01ebba405e0b4d178bffd0..d77035a776aa5dc7ac258a0147ffede4d6587920 100644 +--- x/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm ++++ y/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +@@ -216,7 +216,7 @@ bool shouldBeIgnored(QAccessibleInterface *interface) + // Cocoa accessibility does not have an attribute that corresponds to the Invisible/Offscreen + // state. Ignore interfaces with those flags set. + const QAccessible::State state = interface->state(); +- if (state.invisible || state.offscreen || state.invalid) ++ if (state.invisible || state.invalid) + return true; + + // Some roles are not interesting. In particular, container roles should be diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0003-Android-Unify-behavior-of-Accessible.description-wit.patch ausweisapp2-2.4.0/libs/patches/qtbase-0003-Android-Unify-behavior-of-Accessible.description-wit.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0003-Android-Unify-behavior-of-Accessible.description-wit.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0003-Android-Unify-behavior-of-Accessible.description-wit.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,45 @@ +From c3385c499da1af65f4050646f961b1ac66112f20 Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Mon, 9 Jun 2025 12:32:11 +0200 +Subject: Android: Unify behavior of Accessible.description with other + platforms + +On Windows, macOS and iOS both text and description are read out. On +Android the description is only used, when there is no name/text and +the result is used in AccessibilityNodeInfo::setContentDescription +while AccessibilityNodeInfo::setText is ignored. Using the content +description only is right, because Android will prioritize description +over text. So we need to do our own concatenation to use both. +Using ", " as separator looks like the right way, because Android use +it to separate the text from the role too. This was checked by enabling +TalkBack settings -> Advanced settings -> Developer settings -> Display +speech output. + +Fixes: QTBUG-128494 +Pick-to: 6.10 +Change-Id: Ib25e993ffc8614b9c9ccdc37207f2c3c496ecb1f +Reviewed-by: Assam Boudjelthia +(cherry picked from commit ce101c2c3c04f5ccd46abf7024662d0bc4e5a3a3) +--- + src/plugins/platforms/android/androidjniaccessibility.cpp | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index 73304a9f20178129d966f8a8af48a0f6f5c7f020..4795fffe3143f33e5fc2e142cb67cda55de159c4 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -559,8 +559,12 @@ namespace QtAndroidAccessibility + if (iface && iface->isValid()) { + bool hasValue = false; + desc = iface->text(QAccessible::Name); +- if (desc.isEmpty()) +- desc = iface->text(QAccessible::Description); ++ const QString descStr = iface->text(QAccessible::Description); ++ if (!descStr.isEmpty()) { ++ if (!desc.isEmpty()) ++ desc.append(QStringLiteral(", ")); ++ desc.append(descStr); ++ } + if (desc.isEmpty()) { + desc = iface->text(QAccessible::Value); + hasValue = !desc.isEmpty(); diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0003-Ask-to-scroll-to-on-focus-changes-in-iOS-VoiceOver.patch ausweisapp2-2.4.0/libs/patches/qtbase-0003-Ask-to-scroll-to-on-focus-changes-in-iOS-VoiceOver.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0003-Ask-to-scroll-to-on-focus-changes-in-iOS-VoiceOver.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0003-Ask-to-scroll-to-on-focus-changes-in-iOS-VoiceOver.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,252 +0,0 @@ -From 6b49c155764ea66813f85d9ecd83d783023c7cb3 Mon Sep 17 00:00:00 2001 -From: Jan Moeller -Date: Mon, 4 Dec 2023 13:06:12 +0100 -Subject: Ask to scroll-to on focus changes in iOS VoiceOver - -By adding the UIAccessibilityElementFocusedObserver, the QML Item that -received the focus from VoiceOver is tracked. In turn, the QUIView -will try to scroll the view to bring the newly focused Item into view. -This will enhance the a11y of Flickable/ListView based UI components. -The approach tries to find the first Item with the ScrollBar role and -calls the corresponding scroll action on it, assuming the Item will be -the most useful Item to have a proper attached a11y property. - -Task-number: QTBUG-119724 -Pick-to: 6.8 6.9 -Change-Id: I7632a8dd320af7547b9d36e39f5dce7e266023a4 ---- - .../platforms/ios/qiosplatformaccessibility.h | 6 ++ - .../ios/qiosplatformaccessibility.mm | 38 +++++++ - src/plugins/platforms/ios/qioswindow.h | 1 + - src/plugins/platforms/ios/qioswindow.mm | 5 + - src/plugins/platforms/ios/quiview.h | 1 + - .../platforms/ios/quiview_accessibility.mm | 102 ++++++++++++++++++ - 6 files changed, 153 insertions(+) - -diff --git x/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.h y/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.h -index 96efc663bac896adb44a80eb5953a57ec7d92381..b8e14c14d19a807824384d4b32cce0554752506f 100644 ---- x/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.h -+++ y/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.h -@@ -10,6 +10,8 @@ - - QT_BEGIN_NAMESPACE - -+@class UIAccessibilityElementFocusedObserver; -+ - class QIOSPlatformAccessibility: public QPlatformAccessibility - { - public: -@@ -17,6 +19,10 @@ public: - ~QIOSPlatformAccessibility(); - - virtual void notifyAccessibilityUpdate(QAccessibleEvent *event); -+ -+private: -+ UIAccessibilityElementFocusedObserver* const m_FocusObserver; -+ - }; - - QT_END_NAMESPACE -diff --git x/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.mm y/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.mm -index eb18ee637eef978e8574593644652271896a2035..d79eb8203091c6b684758803c0d03b5a69e4bed7 100644 ---- x/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.mm -+++ y/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.mm -@@ -11,7 +11,45 @@ - #include "qioswindow.h" - #include "quiaccessibilityelement.h" - -+@interface UIAccessibilityElementFocusedObserver : NSObject -+ - (instancetype) init; -+ - (void) receiveNotification: (NSNotification*) notification; -+@end -+ -+@implementation UIAccessibilityElementFocusedObserver -+- (instancetype) init { -+ self = [super init]; -+ if (!self) -+ { -+ return nil; -+ } -+ -+ [[NSNotificationCenter defaultCenter] -+ addObserver:self -+ selector:@selector(receiveNotification:) -+ name:UIAccessibilityElementFocusedNotification -+ object:nil]; -+ -+ return self; -+} -+ -+- (void)receiveNotification:(NSNotification*)notification { -+ const auto element = notification.userInfo[UIAccessibilityFocusedElementKey]; -+ if (!element) -+ return; -+ -+ foreach (QWindow *win, QGuiApplication::topLevelWindows()) { -+ if (win && win->handle()) { -+ QT_PREPEND_NAMESPACE(QIOSWindow) *window = static_cast(win->handle()); -+ if (window->bringIntoView(element)) -+ return; -+ } -+ } -+} -+@end -+ - QIOSPlatformAccessibility::QIOSPlatformAccessibility() -+ : m_FocusObserver([[UIAccessibilityElementFocusedObserver alloc] init]) - {} - - QIOSPlatformAccessibility::~QIOSPlatformAccessibility() -diff --git x/qtbase/src/plugins/platforms/ios/qioswindow.h y/qtbase/src/plugins/platforms/ios/qioswindow.h -index 86bcc111d354c7ce27fc4c29440932462642a116..e7549ee6699289fc218d0f4dde642f68c227e9a9 100644 ---- x/qtbase/src/plugins/platforms/ios/qioswindow.h -+++ y/qtbase/src/plugins/platforms/ios/qioswindow.h -@@ -50,6 +50,7 @@ public: - WId winId() const override { return WId(m_view); } - - void clearAccessibleCache(); -+ bool bringIntoView(id element); - - QSurfaceFormat format() const override; - -diff --git x/qtbase/src/plugins/platforms/ios/qioswindow.mm y/qtbase/src/plugins/platforms/ios/qioswindow.mm -index b11d999252998c97a7fee2df5170a8d49d5dc5a7..6c23061ca0d7f60ade6b8e178471cf708245161e 100644 ---- x/qtbase/src/plugins/platforms/ios/qioswindow.mm -+++ y/qtbase/src/plugins/platforms/ios/qioswindow.mm -@@ -424,6 +424,11 @@ void QIOSWindow::clearAccessibleCache() - [quiview_cast(m_view) clearAccessibleCache]; - } - -+bool QIOSWindow::bringIntoView(id element) -+{ -+ return [quiview_cast(m_view) bringIntoView:element]; -+} -+ - void QIOSWindow::requestUpdate() - { - static_cast(screen())->setUpdatesPaused(false); -diff --git x/qtbase/src/plugins/platforms/ios/quiview.h y/qtbase/src/plugins/platforms/ios/quiview.h -index 7899ec6e0e4212f9ce808d4e3cc44d17b4574c73..01d79bd97b14b7926213fa078e4250f8bdae1c48 100644 ---- x/qtbase/src/plugins/platforms/ios/quiview.h -+++ y/qtbase/src/plugins/platforms/ios/quiview.h -@@ -26,6 +26,7 @@ QT_END_NAMESPACE - - @interface QUIView (Accessibility) - - (void)clearAccessibleCache; -+- (bool)bringIntoView:(id) element; - @end - - @interface UIView (QtHelpers) -diff --git x/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm y/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm -index a95610614be220e241dad30ff3cda34b562ab791..9a2ed1e1b39b5d409059e2bd8db8f8a0129a0844 100644 ---- x/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm -+++ y/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm -@@ -55,6 +55,108 @@ - [m_accessibleElements removeAllObjects]; - } - -+bool performAction(QAccessibleInterface* iface, const QString& actionName) -+{ -+ if (!iface || actionName.isEmpty()) -+ return NO; -+ -+ QAccessibleActionInterface *action = iface->actionInterface(); -+ if (!action) -+ return NO; -+ -+ const auto actionNames = action->actionNames(); -+ if (actionNames.contains(actionName)) { -+ action->doAction(actionName); -+ return YES; -+ } -+ -+ return NO; -+} -+ -+bool performActionUpstream(QAccessibleInterface* iface, const QString& actionName) -+{ -+ if (!iface) -+ return NO; -+ -+ if (performAction(iface, actionName)) -+ return YES; -+ -+ return performActionUpstream(iface->parent(), actionName); -+} -+ -+QAccessibleInterface* findScrollable(QAccessibleInterface* iface) -+{ -+ if (!iface) -+ return nil; -+ -+ if (iface->role() == QAccessible::ScrollBar) -+ return iface; -+ -+ return nil; -+} -+ -+QAccessibleInterface* findScrollableUpstream(QAccessibleInterface* iface) -+{ -+ if (!iface) -+ return nil; -+ -+ for (int i = 0; i < iface->childCount(); ++i) { -+ QAccessibleInterface* scrollable = findScrollable(iface->child(i)); -+ if (scrollable) -+ return scrollable; -+ } -+ -+ return findScrollableUpstream(iface->parent()); -+} -+ -+QString findMoveAction(const QRect& rect, const QRect& boundingRect) -+{ -+ if (rect.x() + rect.width() > boundingRect.x() + boundingRect.width()) -+ return QAccessibleActionInterface::scrollRightAction(); -+ if (rect.x() < boundingRect.x()) -+ return QAccessibleActionInterface::scrollLeftAction(); -+ if (rect.y() + rect.height() > boundingRect.y() + boundingRect.height()) -+ return QAccessibleActionInterface::scrollDownAction(); -+ if (rect.y() < boundingRect.y()) -+ return QAccessibleActionInterface::scrollUpAction(); -+ -+ return QString(); -+} -+ -+- (bool)bringIntoView:(id) element -+{ -+ QMacAccessibilityElement* a11yElement = element; -+ if (!a11yElement) -+ return NO; -+ -+ if (!a11yElement.isAccessibilityElement) -+ return NO; -+ -+ QAccessibleInterface* iface = QAccessible::accessibleInterface(a11yElement.axid); -+ if (!iface || iface->state().invisible) -+ return NO; -+ -+ QAccessibleInterface* scrollable = findScrollable(iface); -+ if (!scrollable) -+ scrollable = findScrollableUpstream(iface->parent()); -+ -+ const QRect elementRect = iface->rect(); -+ -+ if (scrollable) { -+ const QString action = findMoveAction(elementRect, scrollable->rect()); -+ return performAction(scrollable, action); -+ } -+ -+ // Fallback to window to do SOMETHING -+ QWindow* window = iface->window(); -+ if (!window) -+ return NO; -+ -+ const QRect boundingRect = {window->x(), window->y(), window->width(), window->height()}; -+ const QString action = findMoveAction(elementRect, boundingRect); -+ return performActionUpstream(iface, action); -+} -+ - // this is a container, returning yes here means the functions below will never be called - - (BOOL)isAccessibilityElement - { diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0004-Revert-Android-Detect-mouse-buttons.patch ausweisapp2-2.4.0/libs/patches/qtbase-0004-Revert-Android-Detect-mouse-buttons.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0004-Revert-Android-Detect-mouse-buttons.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0004-Revert-Android-Detect-mouse-buttons.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,167 +0,0 @@ -From 7c7a583aeaebcc027d7a13a3785fb271aaa9f8e4 Mon Sep 17 00:00:00 2001 -From: Jan Moeller -Date: Wed, 26 Jun 2024 15:26:09 +0200 -Subject: Revert "Android: Detect mouse buttons" - -This reverts commit 8d8cbe87e21f05b7d611ed4be47299977288b267. ---- - .../qtproject/qt/android/QtInputDelegate.java | 8 +- - .../platforms/android/androidjniinput.cpp | 86 +++---------------- - 2 files changed, 15 insertions(+), 79 deletions(-) - -diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java -index 1fb25b5fe988c2c787aa5cdb3f12a04790a358b9..1d249f517f834b06d9e4166ef27657c534fb68d9 100644 ---- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java -+++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java -@@ -439,8 +439,8 @@ class QtInputDelegate implements QtInputConnection.QtInputConnectionListener, Qt - // tablet methods - - // pointer methods -- static native void mouseDown(int winId, int x, int y, int mouseButtonState); -- static native void mouseUp(int winId, int x, int y, int mouseButtonState); -+ static native void mouseDown(int winId, int x, int y); -+ static native void mouseUp(int winId, int x, int y); - static native void mouseMove(int winId, int x, int y); - static native void mouseWheel(int winId, int x, int y, float hDelta, float vDelta); - static native void touchBegin(int winId); -@@ -556,11 +556,11 @@ class QtInputDelegate implements QtInputConnection.QtInputConnectionListener, Qt - { - switch (event.getActionMasked()) { - case MotionEvent.ACTION_UP: -- mouseUp(id, (int) event.getX(), (int) event.getY(), event.getButtonState()); -+ mouseUp(id, (int) event.getX(), (int) event.getY()); - break; - - case MotionEvent.ACTION_DOWN: -- mouseDown(id, (int) event.getX(), (int) event.getY(), event.getButtonState()); -+ mouseDown(id, (int) event.getX(), (int) event.getY()); - m_oldX = (int) event.getX(); - m_oldY = (int) event.getY(); - break; -diff --git x/qtbase/src/plugins/platforms/android/androidjniinput.cpp y/qtbase/src/plugins/platforms/android/androidjniinput.cpp -index a0faedcc5b2f8f91e55d0434d81b0b5a0fa920d1..3d03b8e69d7a8366f62e1c34aee1007e972673a5 100644 ---- x/qtbase/src/plugins/platforms/android/androidjniinput.cpp -+++ y/qtbase/src/plugins/platforms/android/androidjniinput.cpp -@@ -28,8 +28,6 @@ Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface - namespace QtAndroidInput - { - static bool m_ignoreMouseEvents = false; -- static Qt::MouseButtons m_buttons = Qt::NoButton; -- - static QRect m_softwareKeyboardRect; - - static QList m_touchPoints; -@@ -95,72 +93,7 @@ namespace QtAndroidInput - editButtons, cursor.x(), cursor.y(), anchor.x(), anchor.y(), rtl); - } - -- // from https://developer.android.com/reference/android/view/MotionEvent#getButtonState() -- enum AndroidMouseButton { -- BUTTON_PRIMARY = 0x00000001, -- BUTTON_SECONDARY = 0x00000002, -- BUTTON_TERTIARY = 0x00000004, -- BUTTON_BACK = 0x00000008, -- BUTTON_FORWARD = 0x00000010, -- BUTTON_STYLUS_PRIMARY = 0x00000020, -- BUTTON_STYLUS_SECONDARY = 0x00000040, -- }; -- Q_DECLARE_FLAGS(AndroidMouseButtons, AndroidMouseButton) -- -- static Qt::MouseButtons toMouseButtons(jint j_buttons) -- { -- const auto buttons = static_cast(j_buttons); -- Qt::MouseButtons mouseButtons; -- if (buttons.testFlag(BUTTON_PRIMARY)) -- mouseButtons.setFlag(Qt::LeftButton); -- -- if (buttons.testFlag(BUTTON_SECONDARY)) -- mouseButtons.setFlag(Qt::RightButton); -- -- if (buttons.testFlag(BUTTON_TERTIARY)) -- mouseButtons.setFlag(Qt::MiddleButton); -- -- if (buttons.testFlag(BUTTON_BACK)) -- mouseButtons.setFlag(Qt::BackButton); -- -- if (buttons.testFlag(BUTTON_FORWARD)) -- mouseButtons.setFlag(Qt::ForwardButton); -- -- if (buttons.testFlag(BUTTON_STYLUS_PRIMARY)) -- mouseButtons.setFlag(Qt::LeftButton); -- -- if (buttons.testFlag(BUTTON_STYLUS_SECONDARY)) -- mouseButtons.setFlag(Qt::RightButton); -- -- // Fall back to left button -- if (Q_UNLIKELY(buttons != 0 && mouseButtons == Qt::NoButton)) { -- qWarning() << "Unhandled button value:" << buttons << "Falling back to Qt::LeftButton"; -- mouseButtons = Qt::LeftButton; -- } -- return mouseButtons; -- } -- -- static void sendMouseButtonEvents(QWindow *topLevel, QPoint localPos, QPoint globalPos, -- jint mouseButtonState, QEvent::Type type) -- { -- const Qt::MouseButtons mouseButtons = toMouseButtons(mouseButtonState); -- const Qt::MouseButtons changedButtons = mouseButtons & ~m_buttons; -- -- if (changedButtons == Qt::NoButton) -- return; -- -- static_assert (sizeof(changedButtons) <= sizeof(uint), "Qt::MouseButtons size changed. Adapt code."); -- -- for (uint buttonInt = 0x1; static_cast(changedButtons) >= buttonInt; buttonInt <<= 1) { -- const auto button = static_cast(buttonInt); -- if (changedButtons.testFlag(button)) { -- QWindowSystemInterface::handleMouseEvent(topLevel, localPos, globalPos, -- mouseButtons, button, type); -- } -- } -- } -- -- static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y, jint mouseButtonState) -+ static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y) - { - if (m_ignoreMouseEvents) - return; -@@ -169,11 +102,13 @@ namespace QtAndroidInput - QWindow *window = windowFromId(winId); - m_mouseGrabber = window; - const QPoint localPos = window && window->handle() ? -- window->handle()->mapFromGlobal(globalPos) : globalPos; -- sendMouseButtonEvents(window, localPos, globalPos, mouseButtonState, QEvent::MouseButtonPress); -+ window->handle()->mapFromGlobal(globalPos) : globalPos; -+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, -+ Qt::MouseButtons(Qt::LeftButton), -+ Qt::LeftButton, QEvent::MouseButtonPress); - } - -- static void mouseUp(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y, jint mouseButtonState) -+ static void mouseUp(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y) - { - const QPoint globalPos(x,y); - QWindow *window = m_mouseGrabber.data(); -@@ -182,8 +117,9 @@ namespace QtAndroidInput - - const QPoint localPos = window && window->handle() ? - window->handle()->mapFromGlobal(globalPos) : globalPos; -- -- sendMouseButtonEvents(window, localPos, globalPos, mouseButtonState, QEvent::MouseButtonRelease); -+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, -+ Qt::MouseButtons(Qt::NoButton), -+ Qt::LeftButton, QEvent::MouseButtonRelease); - m_ignoreMouseEvents = false; - m_mouseGrabber.clear(); - } -@@ -907,8 +843,8 @@ namespace QtAndroidInput - {"touchAdd","(IIIZIIFFFF)V",(void*)touchAdd}, - {"touchEnd","(II)V",(void*)touchEnd}, - {"touchCancel", "(I)V", (void *)touchCancel}, -- {"mouseDown", "(IIII)V", (void *)mouseDown}, -- {"mouseUp", "(IIII)V", (void *)mouseUp}, -+ {"mouseDown", "(III)V", (void *)mouseDown}, -+ {"mouseUp", "(III)V", (void *)mouseUp}, - {"mouseMove", "(III)V", (void *)mouseMove}, - {"mouseWheel", "(IIIFF)V", (void *)mouseWheel}, - {"longPress", "(III)V", (void *)longPress}, diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0004-iOS-Add-dynamic-locking-of-screen-orientation.patch ausweisapp2-2.4.0/libs/patches/qtbase-0004-iOS-Add-dynamic-locking-of-screen-orientation.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0004-iOS-Add-dynamic-locking-of-screen-orientation.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0004-iOS-Add-dynamic-locking-of-screen-orientation.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,83 @@ +From afabf323852ecc09ad9714e144df738560fcb53a Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Tue, 10 Jun 2025 11:24:14 +0200 +Subject: iOS: Add dynamic locking of screen orientation + +This introduces support to dynamically lock the screen orientation on +iOS by taking QWindow::contentOrientation into account and mapping +these enums to UIInterfaceOrientationMask. + +Fixes: QTBUG-38576 +Pick-to: 6.10 6.9 +Change-Id: Ibc9a374c8baff02aed4e727ee2c0c30cf319ee01 +--- + .../platforms/ios/qiosviewcontroller.h | 1 + + .../platforms/ios/qiosviewcontroller.mm | 25 ++++++++++++++++++- + src/plugins/platforms/ios/qioswindow.mm | 6 +++++ + 3 files changed, 31 insertions(+), 1 deletion(-) + +diff --git x/qtbase/src/plugins/platforms/ios/qiosviewcontroller.h y/qtbase/src/plugins/platforms/ios/qiosviewcontroller.h +index b5ce88d6a3338d67d70ff35ed4b3069034f292e7..e0968ff7454f481e7306f71cf167a689f9caa4b2 100644 +--- x/qtbase/src/plugins/platforms/ios/qiosviewcontroller.h ++++ y/qtbase/src/plugins/platforms/ios/qiosviewcontroller.h +@@ -24,6 +24,7 @@ QT_END_NAMESPACE + @property (nonatomic, assign) BOOL prefersStatusBarHidden; + @property (nonatomic, assign) UIStatusBarAnimation preferredStatusBarUpdateAnimation; + @property (nonatomic, assign) UIStatusBarStyle preferredStatusBarStyle; ++@property (nonatomic, readonly) UIInterfaceOrientationMask supportedInterfaceOrientations; + #endif + + @end +diff --git x/qtbase/src/plugins/platforms/ios/qiosviewcontroller.mm y/qtbase/src/plugins/platforms/ios/qiosviewcontroller.mm +index 169f8ae17547fbad56dc6c49a5eb284f6c4bfd75..454e66981fdff708464c2a95bfea0ffe6bbb30b4 100644 +--- x/qtbase/src/plugins/platforms/ios/qiosviewcontroller.mm ++++ y/qtbase/src/plugins/platforms/ios/qiosviewcontroller.mm +@@ -531,7 +531,30 @@ + shortcutMap.tryShortcut(&keyEvent); + } + +- ++- (UIInterfaceOrientationMask)supportedInterfaceOrientations ++{ ++ QWindow *window = nullptr; ++ QWindow *focusWindow = QGuiApplication::focusWindow(); ++ if (focusWindow && focusWindow->screen() ++ && focusWindow->screen()->handle() == self.platformScreen) ++ window = qt_window_private(focusWindow)->topLevelWindow(); ++ ++ if (!window || window->contentOrientation() == Qt::PrimaryOrientation) ++ return UIInterfaceOrientationMaskAll; ++ ++ const auto contentOrientation = window->contentOrientation(); ++ NSInteger supportedOrientations = 0; ++ if (contentOrientation & Qt::PortraitOrientation) ++ supportedOrientations |= UIInterfaceOrientationMaskPortrait; ++ if (contentOrientation & Qt::InvertedPortraitOrientation) ++ supportedOrientations |= UIInterfaceOrientationMaskPortraitUpsideDown; ++ if (contentOrientation & Qt::LandscapeOrientation) ++ supportedOrientations |= UIInterfaceOrientationMaskLandscapeLeft; ++ if (contentOrientation & Qt::InvertedLandscapeOrientation) ++ supportedOrientations |= UIInterfaceOrientationMaskLandscapeRight; ++ ++ return supportedOrientations; ++} + + @end + +diff --git x/qtbase/src/plugins/platforms/ios/qioswindow.mm y/qtbase/src/plugins/platforms/ios/qioswindow.mm +index af1fbb0abaf1f937d4b3bc6991e39bc104fd380e..1dff0e6a526541425cbbc55d9e4b86d13554fef1 100644 +--- x/qtbase/src/plugins/platforms/ios/qioswindow.mm ++++ y/qtbase/src/plugins/platforms/ios/qioswindow.mm +@@ -81,6 +81,12 @@ QIOSWindow::QIOSWindow(QWindow *window, WId nativeHandle) + handleContentOrientationChange(initialOrientation); + }); + } ++ ++ connect(window, &QWindow::contentOrientationChanged, this, ++ [this](Qt::ScreenOrientation orientation) { ++ Q_UNUSED(orientation;) ++ [m_view.qtViewController setNeedsUpdateOfSupportedInterfaceOrientations]; ++ }); + } + + QIOSWindow::~QIOSWindow() diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0005-Android-Set-name-matched-class-names-for-Slider-and-.patch ausweisapp2-2.4.0/libs/patches/qtbase-0005-Android-Set-name-matched-class-names-for-Slider-and-.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0005-Android-Set-name-matched-class-names-for-Slider-and-.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0005-Android-Set-name-matched-class-names-for-Slider-and-.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,41 @@ +From 22c13ee169ef0a781c1f11111bfda165d842abdf Mon Sep 17 00:00:00 2001 +From: Timon Sassor +Date: Tue, 19 Aug 2025 18:12:12 +0200 +Subject: Android: Set name-matched class names for Slider and ScrollBar for + TalkBack + +Seekbar is not the ideal class name for ScrollBar and Slider. It changes +the behavior of these Element, so that the scroll actions are not +properly delivered to them by TalkBack. We use the Android class name +that best match the QAccessible role name. We accept, that TalkBack has +no implementation for them and will use the default behavior of +Android.view. + +[ChangeLog][Accessibility][Android] Use different Android class names +for Slider and Scroller. + +Pick-to: 6.10 6.9 6.8 +Change-Id: I03d9ce2bd051ff2c330852636d92e47c6d971884 +Reviewed-by: Assam Boudjelthia +(cherry picked from commit b682d31d7e58cd74bb32df2fc984325f0825020e) +--- + src/plugins/platforms/android/androidjniaccessibility.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index 4795fffe3143f33e5fc2e142cb67cda55de159c4..64a1065f32121e1cc7466a9b7ec07cc1c8f6a1f4 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -485,9 +485,10 @@ namespace QtAndroidAccessibility + return QStringLiteral("android.app.ActionBar.Tab"); + case QAccessible::Role::PageTabList: + return QStringLiteral("android.widget.TabWidget"); +- case QAccessible::Role::ScrollBar: [[fallthrough]]; ++ case QAccessible::Role::ScrollBar: ++ return QStringLiteral("android.widget.Scroller"); + case QAccessible::Role::Slider: +- return QStringLiteral("android.widget.SeekBar"); ++ return QStringLiteral("com.google.android.material.slider.Slider"); + case QAccessible::Role::Table: + // #TODO Evaluate the usage of AccessibleNodeInfo.setCollectionItemInfo() to provide + // infos about colums, rows und items. diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0005-QThread-Unix-do-clean-up-the-QAdoptedThread-for-the-.patch ausweisapp2-2.4.0/libs/patches/qtbase-0005-QThread-Unix-do-clean-up-the-QAdoptedThread-for-the-.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0005-QThread-Unix-do-clean-up-the-QAdoptedThread-for-the-.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0005-QThread-Unix-do-clean-up-the-QAdoptedThread-for-the-.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,161 +0,0 @@ -From 9b7450da2b01cd7a8c9e201329d75ea6d4b9ea7b Mon Sep 17 00:00:00 2001 -From: Thiago Macieira -Date: Tue, 5 Nov 2024 15:50:12 -0800 -Subject: QThread/Unix: do clean up the QAdoptedThread for the main thread - -Commit 1ed0dd88a32cd2c5ae100b48e14ff55bcbb652e6 ("QThread/Unix: make -QThreadPrivate::finish() be called much later") introduced this problem. -Commit 4fabde349f16b59f37568da2a4c050c6dd53a34e split the thread -termination in two phases, but did not fix this. - -This re-applies commit 950b35cf97ad398f97883efd2a18ee97994a8a9c ("Clear -the current thread data for the main thread"), which was reverted in -commit 7dc622290bb8e81af634034f443e25be0d6d48a3 ("Make sure QThreadData -and QAdoptedThread object is destroyed at app exit"), both from Qt 5.1. - -Between Qt 5.1 and 6.7, the responsibility of clearing the -QAdoptedThread for the main thread was split: it could occur either in -~QCoreApplicationData if exit() was called in that thread or in -~QThreadData() if it wasn't (e.g., when the Qt "main thread" is not -main()'s thread): - * frame #0: 0x0000000101db8a28 QtCore`QAdoptedThread::~QAdoptedThread(this=0x000060000176c070) at qthread.cpp:139:1 - frame #1: 0x0000000101db81eb QtCore`QThreadData::~QThreadData(this=0x0000600002468000) at qthread.cpp:82:5 - frame #2: 0x0000000101db8379 QtCore`QThreadData::~QThreadData(this=0x0000600002468000) at qthread.cpp:57:1 - frame #3: 0x0000000101db841c QtCore`QThreadData::deref(this=0x0000600002468000) at qthread.cpp:108:9 - frame #4: 0x0000000101f4ec79 QtCore`destroy_current_thread_data(p=0x0000600002468000) at qthread_unix.cpp:104:11 - -This commit centralizes and gives ~QThreadData() the exclusive -responsibility. That requires not resetting QThreadData::threadId so -~QThreadData can know it is theMainThread. - -Fixes: QTBUG-130895 -Task-number: QTBUG-129927 -Task-number: QTBUG-129846 -Task-number: QTBUG-130341 -Task-number: QTBUG-117996 -Pick-to: 6.8 -Change-Id: Ie3f3cbdc5523837b505cfffd95fba5e6498b5069 -Reviewed-by: Ivan Solovev -(cherry picked from commit 65093a84c2b94b1543fd4593bc45d491951d28d4) ---- - src/corelib/kernel/qcoreapplication.cpp | 6 +++-- - src/corelib/thread/qthread_unix.cpp | 24 ++++++++++++------- - .../qcoreapplication/tst_qcoreapplication.cpp | 19 ++++++++++++--- - 3 files changed, 35 insertions(+), 14 deletions(-) - -diff --git x/qtbase/src/corelib/kernel/qcoreapplication.cpp y/qtbase/src/corelib/kernel/qcoreapplication.cpp -index ba069c40408ed42eafbdc690ad707b1744d41144..faf3b87c2276cc839a3c81b0801954f1da24e431 100644 ---- x/qtbase/src/corelib/kernel/qcoreapplication.cpp -+++ y/qtbase/src/corelib/kernel/qcoreapplication.cpp -@@ -364,14 +364,16 @@ Q_CONSTINIT uint QCoreApplicationPrivate::attribs = - (1 << Qt::AA_SynthesizeMouseForUnhandledTouchEvents) | - (1 << Qt::AA_SynthesizeMouseForUnhandledTabletEvents); - --struct QCoreApplicationData { -+struct QCoreApplicationData -+{ - QCoreApplicationData() noexcept { - applicationNameSet = false; - applicationVersionSet = false; - } - ~QCoreApplicationData() { --#ifndef QT_NO_QOBJECT -+#if !defined(QT_NO_QOBJECT) && defined(Q_OS_WIN) - // cleanup the QAdoptedThread created for the main() thread -+ // (for Unix systems, see qthread_unix.cpp:set_thread_data()) - if (auto *t = QCoreApplicationPrivate::theMainThread.loadAcquire()) { - QThreadData *data = QThreadData::get2(t); - data->deref(); // deletes the data and the adopted thread -diff --git x/qtbase/src/corelib/thread/qthread_unix.cpp y/qtbase/src/corelib/thread/qthread_unix.cpp -index 14c6490c1e7ba09a36bfdfe3c6311fae442ae70d..36112afd4dfa00ac973ffdb2c7a89e40a73ffa42 100644 ---- x/qtbase/src/corelib/thread/qthread_unix.cpp -+++ y/qtbase/src/corelib/thread/qthread_unix.cpp -@@ -140,12 +140,20 @@ static QThreadData *get_thread_data() - return currentThreadData; - } - -+#if QT_CONFIG(broken_threadlocal_dtors) -+// The destructors registered with pthread_key_create() below are NOT run from -+// exit(), so we must also use atexit(). -+static void destroy_main_thread_data() -+{ -+ if (QThreadData *d = get_thread_data()) -+ destroy_current_thread_data(d); -+} -+Q_DESTRUCTOR_FUNCTION(destroy_main_thread_data) -+#endif -+ - static void set_thread_data(QThreadData *data) - { -- // Only activate the late cleanup for auxiliary threads. We can't use -- // QThread::isMainThread() here because theMainThreadId will not have been -- // set yet. -- if (data && QCoreApplicationPrivate::theMainThreadId.loadAcquire()) { -+ if (data) { - if constexpr (QT_CONFIG(broken_threadlocal_dtors)) { - static pthread_key_t tls_key; - struct TlsKey { -@@ -418,7 +426,6 @@ void QThreadPrivate::cleanup() - d->interruptionRequested.store(false, std::memory_order_relaxed); - - d->isInFinish = false; -- d->data->threadId.storeRelaxed(nullptr); - - d->thread_done.wakeAll(); - }); -@@ -828,14 +835,14 @@ bool QThread::wait(QDeadlineTimer deadline) - Q_D(QThread); - QMutexLocker locker(&d->mutex); - -+ if (d->finished || !d->running) -+ return true; -+ - if (isCurrentThread()) { - qWarning("QThread::wait: Thread tried to wait on itself"); - return false; - } - -- if (d->finished || !d->running) -- return true; -- - return d->wait(locker, deadline); - } - -@@ -848,7 +855,6 @@ bool QThreadPrivate::wait(QMutexLocker &locker, QDeadlineTimer deadline) - if (!d->thread_done.wait(locker.mutex(), deadline)) - return false; - } -- Q_ASSERT(d->data->threadId.loadRelaxed() == nullptr); - - return true; - } -diff --git x/qtbase/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp y/qtbase/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp -index 5dc445d44f211f31d6a17212b09c953fcf1a8c5a..a031e6a54d0d3ff3734de06dbb1cb4e9d1a2ff9c 100644 ---- x/qtbase/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp -+++ y/qtbase/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp -@@ -1065,9 +1065,22 @@ static void createQObjectOnDestruction() - // QThread) after the last QObject has been destroyed (especially after - // QCoreApplication has). - --#if !defined(QT_QGUIAPPLICATIONTEST) && !defined(Q_OS_WIN) && !defined(Q_OS_VXWORKS) -- // QCoreApplicationData's global static destructor has run and cleaned up -- // the QAdoptedThread. -+#if defined(QT_QGUIAPPLICATIONTEST) -+ // If we've linked to QtGui, we make no representations about there being -+ // global static (not Q_GLOBAL_STATIC) variables that are QObject. -+#elif QT_CONFIG(broken_threadlocal_dtors) -+ // With broken thread-local destructors, we cannot guarantee the ordering -+ // between thread_local destructors and static-lifetime destructors (hence -+ // why they're broken). -+ // -+ // On Unix systems, we use a Q_DESTRUCTOR_FUNCTION in qthread_unix.cpp to -+ // work around the issue, but that means it cannot have run yet. -+ // -+ // This variable is set on Windows too, even though the nature of the -+ // problem is different. -+#else -+ // The thread_local destructor in qthread_unix.cpp has run so the -+ // QAdoptedThread must have been cleaned up. - if (theMainThreadIsSet()) - qFatal("theMainThreadIsSet() returned true; some QObject must have leaked"); - #endif diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0006-Add-iOS-UIScene-related-URL-handling.patch ausweisapp2-2.4.0/libs/patches/qtbase-0006-Add-iOS-UIScene-related-URL-handling.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0006-Add-iOS-UIScene-related-URL-handling.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0006-Add-iOS-UIScene-related-URL-handling.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,115 +0,0 @@ -From 2d622bade7dd58c807dc1fe0eec4270c52064123 Mon Sep 17 00:00:00 2001 -From: Juha Vuolle -Date: Tue, 3 Dec 2024 10:11:11 +0200 -Subject: Add iOS UIScene related URL handling -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Qt adopted UIScene lifecycle in Qt 6.8. As part of that switch, -the application URL delivery also changes to scene-specific, and the -previous QIOSApplicationDelegate URL handling callbacks aren't invoked -anymore. - -This commit adds the URL handling to these scene-based callbacks: -willConnectToSession, openURLContexts, and continueUserActivity. -The older callbacks are removed as unnecessary. - -With these additions, the application is capable of handling -custom-uri-schemes and https universal links, both when it's already -running, and when it needs to be launched. - -Amends: 76ebf51bc08f6af624a8540e7af88b9129b22ae1 - -Pick-to: 6.8 -Fixes: QTBUG-131741 -Change-Id: Icacd76e9769f0a559b2052dfb60466a871187321 -Reviewed-by: Tor Arne Vestbø -(cherry picked from commit c81f805817c82018e133ca140183f7ecee9d6130) ---- - .../platforms/ios/qiosapplicationdelegate.mm | 65 +++++++++---------- - 1 file changed, 29 insertions(+), 36 deletions(-) - -diff --git x/qtbase/src/plugins/platforms/ios/qiosapplicationdelegate.mm y/qtbase/src/plugins/platforms/ios/qiosapplicationdelegate.mm -index c6e5a83874157aa7c4d45ebe823add979b7ab5a6..088d48fc9ffbbc924ddeddbed92edcabefffc488 100644 ---- x/qtbase/src/plugins/platforms/ios/qiosapplicationdelegate.mm -+++ y/qtbase/src/plugins/platforms/ios/qiosapplicationdelegate.mm -@@ -20,42 +20,6 @@ - - @implementation QIOSApplicationDelegate - --- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> *restorableObjects))restorationHandler --{ -- Q_UNUSED(application); -- Q_UNUSED(restorationHandler); -- -- if (!QGuiApplication::instance()) -- return NO; -- -- if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { -- QIOSIntegration *iosIntegration = QIOSIntegration::instance(); -- Q_ASSERT(iosIntegration); -- -- QIOSServices *iosServices = static_cast(iosIntegration->services()); -- -- return iosServices->handleUrl(QUrl::fromNSURL(userActivity.webpageURL)); -- } -- -- return NO; --} -- --- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options --{ -- Q_UNUSED(application); -- Q_UNUSED(options); -- -- if (!QGuiApplication::instance()) -- return NO; -- -- QIOSIntegration *iosIntegration = QIOSIntegration::instance(); -- Q_ASSERT(iosIntegration); -- -- QIOSServices *iosServices = static_cast(iosIntegration->services()); -- -- return iosServices->handleUrl(QUrl::fromNSURL(url)); --} -- - - (UISceneConfiguration *)application:(UIApplication *)application - configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession - options:(UISceneConnectionOptions *)options -@@ -94,6 +58,35 @@ - - window.rootViewController = [[[QIOSViewController alloc] - initWithWindow:window andScreen:screen] autorelease]; -+ -+ if (connectionOptions.URLContexts.count > 0) -+ [self scene:scene openURLContexts:connectionOptions.URLContexts]; -+} -+ -+- (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts -+{ -+ qCDebug(lcQpaWindowScene) << "Handling openURLContexts for scene" << scene; -+ -+ QIOSIntegration *iosIntegration = QIOSIntegration::instance(); -+ Q_ASSERT(iosIntegration); -+ -+ QIOSServices *iosServices = static_cast(iosIntegration->services()); -+ -+ for (UIOpenURLContext *urlContext in URLContexts) -+ iosServices->handleUrl(QUrl::fromNSURL(urlContext.URL)); -+} -+ -+- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity -+{ -+ qCDebug(lcQpaWindowScene) << "Handling continueUserActivity for scene" << scene; -+ -+ if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { -+ QIOSIntegration *iosIntegration = QIOSIntegration::instance(); -+ Q_ASSERT(iosIntegration); -+ -+ QIOSServices *iosServices = static_cast(iosIntegration->services()); -+ iosServices->handleUrl(QUrl::fromNSURL(userActivity.webpageURL)); -+ } - } - - @end diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0006-a11y-Windows-Implement-UIA_AriaRolePropertyId-for-Ac.patch ausweisapp2-2.4.0/libs/patches/qtbase-0006-a11y-Windows-Implement-UIA_AriaRolePropertyId-for-Ac.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0006-a11y-Windows-Implement-UIA_AriaRolePropertyId-for-Ac.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0006-a11y-Windows-Implement-UIA_AriaRolePropertyId-for-Ac.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,34 @@ +From a0075f2a27929c47f414fd4338bd299758919def Mon Sep 17 00:00:00 2001 +From: Julian Greilich +Date: Fri, 8 Aug 2025 11:16:18 +0200 +Subject: a11y Windows: Implement UIA_AriaRolePropertyId for + Accessible::Heading + +This way also the property UIA_LocalizedControlTypePropertyId is +populated and screen readers like narrator can announce the role of +headings correctly. + +Pick-to: 6.8 6.9 6.10 +Change-Id: I745e4420bbe0f2cc1ef0e52ecdeab781d641b474 +Reviewed-by: Michael Weghorn +Reviewed-by: Volker Hilsheimer +(cherry picked from commit 6dfef0040307f9d78eaa4e1707bd6b58e22d961d) +--- + .../windows/uiautomation/qwindowsuiamainprovider.cpp | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +index 50f5f7b2c7f7d87d11152c6788ab118539f17b70..30471ee68c1eb7868edd21154fd8e35126e8f199 100644 +--- x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp ++++ y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +@@ -503,6 +503,10 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR + // Accelerator key. + *pRetVal = QComVariant{ accessible->text(QAccessible::Accelerator) }.release(); + break; ++ case UIA_AriaRolePropertyId: ++ if (accessible->role() == QAccessible::Heading) ++ *pRetVal = QComVariant{ QStringLiteral("heading") }.release(); ++ break; + case UIA_AriaPropertiesPropertyId: + setAriaProperties(accessible, pRetVal); + break; diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0007-CMake-Android-add-property-QT_ANDROID_COMPILE_SDK_VE.patch ausweisapp2-2.4.0/libs/patches/qtbase-0007-CMake-Android-add-property-QT_ANDROID_COMPILE_SDK_VE.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0007-CMake-Android-add-property-QT_ANDROID_COMPILE_SDK_VE.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0007-CMake-Android-add-property-QT_ANDROID_COMPILE_SDK_VE.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,168 +0,0 @@ -From 148ec3d4a7f16990c518264b80d4419fe781866c Mon Sep 17 00:00:00 2001 -From: Assam Boudjelthia -Date: Sun, 8 Sep 2024 20:22:15 +0300 -Subject: CMake:Android: add property QT_ANDROID_COMPILE_SDK_VERSION - -This allows to set the compile SDK version from CMakeLists.txt -instead of a parameter to androiddeployqt to give more flexibility. - -Fixes: QTBUG-128364 -Change-Id: I797e8f9b3c35dcb822c1b7e2b67e6b76387775ca -Reviewed-by: Alexey Edelev -(cherry picked from commit a9ebb97436b08ac2881c25115d656b6fc6e22764) ---- - src/corelib/Qt6AndroidMacros.cmake | 4 ++ - .../doc/src/cmake/cmake-properties.qdoc | 37 +++++++++++++++++++ - ..._android_generate_deployment_settings.qdoc | 1 + - src/tools/androiddeployqt/main.cpp | 26 +++++++++---- - .../CMakeLists.txt | 2 + - .../tst_android_deployment_settings.cpp | 2 + - 6 files changed, 65 insertions(+), 7 deletions(-) - -diff --git x/qtbase/src/corelib/Qt6AndroidMacros.cmake y/qtbase/src/corelib/Qt6AndroidMacros.cmake -index e192ca78e9a75f2f505f0758ecfaf08ed4d988d3..0a5cd17d6b4a12b771c649f35ad61b4aeda90863 100644 ---- x/qtbase/src/corelib/Qt6AndroidMacros.cmake -+++ y/qtbase/src/corelib/Qt6AndroidMacros.cmake -@@ -237,6 +237,10 @@ function(qt6_android_generate_deployment_settings target) - _qt_internal_add_android_deployment_property(file_contents "android-target-sdk-version" - ${target} "QT_ANDROID_TARGET_SDK_VERSION") - -+ # compile SDK version -+ _qt_internal_add_android_deployment_property(file_contents "android-compile-sdk-version" -+ ${target} "QT_ANDROID_COMPILE_SDK_VERSION") -+ - # should Qt shared libs be excluded from deployment - _qt_internal_add_android_deployment_property(file_contents "android-no-deploy-qt-libs" - ${target} "QT_ANDROID_NO_DEPLOY_QT_LIBS") -diff --git x/qtbase/src/corelib/doc/src/cmake/cmake-properties.qdoc y/qtbase/src/corelib/doc/src/cmake/cmake-properties.qdoc -index 1195f2784c337ea34a79a546ead635db32f76da6..25fb9ead06ff22e136407020555ac516fae2a07f 100644 ---- x/qtbase/src/corelib/doc/src/cmake/cmake-properties.qdoc -+++ y/qtbase/src/corelib/doc/src/cmake/cmake-properties.qdoc -@@ -223,6 +223,43 @@ Specifies the target Android API level for the target. - \sa{qt6_android_generate_deployment_settings}{qt_android_generate_deployment_settings()} - */ - -+/*! -+\page cmake-target-property-qt-android-compile-sdk-version.html -+\ingroup cmake-properties-qtcore -+\ingroup cmake-target-properties-qtcore -+\ingroup cmake-android-build-properties -+ -+\title QT_ANDROID_COMPILE_SDK_VERSION -+\target cmake-target-property-QT_ANDROID_COMPILE_SDK_VERSION -+ -+\summary {Android target SDK version.} -+ -+\cmakepropertysince 6.9 -+\cmakepropertyandroidonly -+ -+Specifies the version of the Android SDK against which your application is compiled. -+This is propagated to \c {build.gradle} as \c compileSdkVersion. This value can be -+provided via \l {androiddeployqt}'s \c {--android-platform} parameter which takes -+precedence over this CMake property. -+ -+\badcode -+set_target_properties(${target} PROPERTIES -+ QT_ANDROID_COMPILE_SDK_VERSION 35 -+) -+\endcode -+ -+The following format also works: -+ -+\badcode -+set_target_properties(${target} PROPERTIES -+ QT_ANDROID_COMPILE_SDK_VERSION "android-35" -+) -+\endcode -+ -+\sa{qt6_android_generate_deployment_settings}{qt_android_generate_deployment_settings()} -+\sa QT_ANDROID_TARGET_SDK_VERSION -+*/ -+ - /*! - \page cmake-target-property-qt-android-sdk-build-tools-revision.html - \ingroup cmake-properties-qtcore -diff --git x/qtbase/src/corelib/doc/src/cmake/qt_android_generate_deployment_settings.qdoc y/qtbase/src/corelib/doc/src/cmake/qt_android_generate_deployment_settings.qdoc -index daa3680070a46d0223e2ab49740e2a99006bdf68..ab9dcfa9821a72e93ba9e1dd895e608701e18847 100644 ---- x/qtbase/src/corelib/doc/src/cmake/qt_android_generate_deployment_settings.qdoc -+++ y/qtbase/src/corelib/doc/src/cmake/qt_android_generate_deployment_settings.qdoc -@@ -63,6 +63,7 @@ how to accomplish this. - \li \l{cmake-target-property-QT_ANDROID_MIN_SDK_VERSION}{QT_ANDROID_MIN_SDK_VERSION} - \li \l{cmake-target-property-QT_ANDROID_PACKAGE_SOURCE_DIR}{QT_ANDROID_PACKAGE_SOURCE_DIR} - \li \l{cmake-target-property-QT_ANDROID_TARGET_SDK_VERSION}{QT_ANDROID_TARGET_SDK_VERSION} -+\li \l{cmake-target-property-QT_ANDROID_COMPILE_SDK_VERSION}{QT_ANDROID_COMPILE_SDK_VERSION} - \li \l{cmake-target-property-QT_ANDROID_PACKAGE_NAME}{QT_ANDROID_PACKAGE_NAME} - \li \l{cmake-target-property-QT_ANDROID_VERSION_NAME}{QT_ANDROID_VERSION_NAME} - \li \l{cmake-target-property-QT_ANDROID_VERSION_CODE}{QT_ANDROID_VERSION_CODE} -diff --git x/qtbase/src/tools/androiddeployqt/main.cpp y/qtbase/src/tools/androiddeployqt/main.cpp -index d8839a0ef6e0c1e5fd88610fec59685cc95b2f07..16912823e9c8b447da232608ffbea9ca614eeac3 100644 ---- x/qtbase/src/tools/androiddeployqt/main.cpp -+++ y/qtbase/src/tools/androiddeployqt/main.cpp -@@ -1022,15 +1022,27 @@ bool readInputFile(Options *options) - - options->sdkPath = QDir::fromNativeSeparators(sdkPath.toString()); - -+ } -+ -+ { - if (options->androidPlatform.isEmpty()) { -- options->androidPlatform = detectLatestAndroidPlatform(options->sdkPath); -- if (options->androidPlatform.isEmpty()) -- return false; -- } else { -- if (!QDir(options->sdkPath + "/platforms/"_L1 + options->androidPlatform).exists()) { -- fprintf(stderr, "Warning: Android platform '%s' does not exist in SDK.\n", -- qPrintable(options->androidPlatform)); -+ const QJsonValue ver = jsonObject.value("android-compile-sdk-version"_L1); -+ if (!ver.isUndefined()) { -+ const auto value = ver.toString(); -+ options->androidPlatform = value.startsWith("android-"_L1) ? -+ value : "android-%1"_L1.arg(value); - } -+ -+ if (options->androidPlatform.isEmpty()) { -+ options->androidPlatform = detectLatestAndroidPlatform(options->sdkPath); -+ if (options->androidPlatform.isEmpty()) -+ return false; -+ } -+ } -+ -+ if (!QDir(options->sdkPath + "/platforms/"_L1 + options->androidPlatform).exists()) { -+ fprintf(stderr, "Warning: Android platform '%s' does not exist in SDK.\n", -+ qPrintable(options->androidPlatform)); - } - } - -diff --git x/qtbase/tests/auto/other/android_deployment_settings/CMakeLists.txt y/qtbase/tests/auto/other/android_deployment_settings/CMakeLists.txt -index 9ef457189a67f0be332ffc2b9c749da192c419a3..4c6dba73c5424f8afd12f7d9bbd37e6baab6b79b 100644 ---- x/qtbase/tests/auto/other/android_deployment_settings/CMakeLists.txt -+++ y/qtbase/tests/auto/other/android_deployment_settings/CMakeLists.txt -@@ -30,6 +30,7 @@ set_target_properties(${target} PROPERTIES - QT_ANDROID_SDK_BUILD_TOOLS_REVISION "23.0.2" - QT_ANDROID_MIN_SDK_VERSION "1" - QT_ANDROID_TARGET_SDK_VERSION "2" -+ QT_ANDROID_COMPILE_SDK_VERSION "35" - QT_ANDROID_PACKAGE_NAME "org.qtproject.android_deployment_settings_test" - QT_ANDROID_DEPLOYMENT_DEPENDENCIES "dep1.so;dep2.so;dep3.so" - QT_ANDROID_DEPLOYMENT_SETTINGS_FILE "attempt_to_rewrite.json" -@@ -54,6 +55,7 @@ set_target_properties(${target} PROPERTIES - QT_ANDROID_SDK_BUILD_TOOLS_REVISION "23.0.2" - QT_ANDROID_MIN_SDK_VERSION "1" - QT_ANDROID_TARGET_SDK_VERSION "2" -+ QT_ANDROID_COMPILE_SDK_VERSION "35" - QT_ANDROID_PACKAGE_NAME "org.qtproject.android_deployment_settings_test" - QT_ANDROID_DEPLOYMENT_DEPENDENCIES "dep1.so;dep2.so;dep3.so" - QT_ANDROID_EXTRA_LIBS -diff --git x/qtbase/tests/auto/other/android_deployment_settings/tst_android_deployment_settings.cpp y/qtbase/tests/auto/other/android_deployment_settings/tst_android_deployment_settings.cpp -index f8428aaf43ca3e626c15d428d18036ffd7b93124..07285cd42c17de8027e6a896d45e3431ec9d5e93 100644 ---- x/qtbase/tests/auto/other/android_deployment_settings/tst_android_deployment_settings.cpp -+++ y/qtbase/tests/auto/other/android_deployment_settings/tst_android_deployment_settings.cpp -@@ -76,6 +76,8 @@ void tst_android_deployment_settings::DeploymentSettings_data() - << "1"; - QTest::newRow("android-target-sdk-version") << "android-target-sdk-version" - << "2"; -+ QTest::newRow("android-compile-sdk-version") << "android-compile-sdk-version" -+ << "35"; - QTest::newRow("android-package-name") << "android-package-name" - << "org.qtproject.android_deployment_settings_test"; - } diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0007-a11y-iOS-Announce-changes-to-name-description-to-Voi.patch ausweisapp2-2.4.0/libs/patches/qtbase-0007-a11y-iOS-Announce-changes-to-name-description-to-Voi.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0007-a11y-iOS-Announce-changes-to-name-description-to-Voi.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0007-a11y-iOS-Announce-changes-to-name-description-to-Voi.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,83 @@ +From a44c1117d89958dd72123d07440281ec91f6f36a Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Tue, 19 Aug 2025 10:17:48 +0200 +Subject: a11y iOS: Announce changes to name/description to VoiceOver + +Unlike macOS AppKit iOS UIKit does not offer an API to notify +VoiceOver about a value/title change. Also there is no public +API to query the system for the element which is currently +focused. + +Both these caveats are addressed in this commit: the element with +active a11y focus is tracked; if the Accessible.name or +Accessible.description for *this* element changes, VoiceOver +is informed via UIAccessibilityLayoutChangedNotification and +refocuses the updated element. + +Task-number: QTBUG-139275 +Pick-to: 6.8 6.9 6.10 +Change-Id: Ief1a2928aac138fe3f7afd9571838ed7713b327e +Reviewed-by: Volker Hilsheimer +(cherry picked from commit 6d0c380ac12c9f87fba464539a788b3185e85fb9) +--- + .../platforms/ios/qiosplatformaccessibility.h | 6 ++++++ + .../platforms/ios/qiosplatformaccessibility.mm | 15 ++++++++++++++- + 2 files changed, 20 insertions(+), 1 deletion(-) + +diff --git x/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.h y/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.h +index 96efc663bac896adb44a80eb5953a57ec7d92381..49cecbfec1f19a33435293f3a389e630c46fcae3 100644 +--- x/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.h ++++ y/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.h +@@ -5,6 +5,8 @@ + #define QIOSPLATFORMACCESSIBILITY_H + + #include ++#include ++#include + + #if QT_CONFIG(accessibility) + +@@ -17,6 +19,10 @@ public: + ~QIOSPlatformAccessibility(); + + virtual void notifyAccessibilityUpdate(QAccessibleEvent *event); ++ ++private: ++ QMacNotificationObserver m_focusObserver; ++ QMacAccessibilityElement *m_focusElement; + }; + + QT_END_NAMESPACE +diff --git x/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.mm y/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.mm +index f661db7a2e19a35dac076970641bce4a72b564b4..6869db5ae81411b42be9d293327acab3a39faec2 100644 +--- x/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.mm ++++ y/qtbase/src/plugins/platforms/ios/qiosplatformaccessibility.mm +@@ -12,7 +12,13 @@ + #include "quiaccessibilityelement.h" + + QIOSPlatformAccessibility::QIOSPlatformAccessibility() +-{} ++{ ++ m_focusObserver = QMacNotificationObserver( ++ nil, UIAccessibilityElementFocusedNotification, [&](NSNotification *notification) { ++ id element = notification.userInfo[UIAccessibilityFocusedElementKey]; ++ m_focusElement = static_cast(element); ++ }); ++} + + QIOSPlatformAccessibility::~QIOSPlatformAccessibility() + {} +@@ -59,6 +65,13 @@ void QIOSPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *even + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, element); + break; + } ++ case QAccessible::DescriptionChanged: ++ case QAccessible::NameChanged: { ++ auto *element = [QMacAccessibilityElement elementWithId:event->uniqueId()]; ++ if (element == m_focusElement) ++ UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, element); ++ break; ++ } + case QAccessible::ObjectCreated: + case QAccessible::ObjectShow: + case QAccessible::ObjectHide: diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0008-Add-_qt_internal_detect_latest_android_platform.patch ausweisapp2-2.4.0/libs/patches/qtbase-0008-Add-_qt_internal_detect_latest_android_platform.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0008-Add-_qt_internal_detect_latest_android_platform.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0008-Add-_qt_internal_detect_latest_android_platform.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -From d857a206a84029695274a3072b010eaf774bf4f2 Mon Sep 17 00:00:00 2001 -From: Alexey Edelev -Date: Thu, 28 Nov 2024 14:33:46 +0100 -Subject: Add _qt_internal_detect_latest_android_platform - -Function generalize the detecting of the latest available Android -platform in the ANDROID_SDK_ROOT directory. - -Change-Id: Ib1d064428c414625f24765b50cff500a0ad5d27e -Reviewed-by: Alexandru Croitor -(cherry picked from commit de40931eba2cf09a8c97c9a75c6d23c77748f44c) ---- - cmake/QtBuildHelpers.cmake | 1 + - cmake/QtPlatformAndroid.cmake | 13 ++----------- - cmake/QtPublicAndroidHelpers.cmake | 20 ++++++++++++++++++++ - 3 files changed, 23 insertions(+), 11 deletions(-) - create mode 100644 cmake/QtPublicAndroidHelpers.cmake - -diff --git x/qtbase/cmake/QtBuildHelpers.cmake y/qtbase/cmake/QtBuildHelpers.cmake -index 66aceac62838421d0f72cd0550747dc78b42eef0..bafca09043f377cc64be63e031bc23384c5555af 100644 ---- x/qtbase/cmake/QtBuildHelpers.cmake -+++ y/qtbase/cmake/QtBuildHelpers.cmake -@@ -278,6 +278,7 @@ function(qt_internal_get_qt_build_public_helpers out_var) - set(${out_var} - QtFeature - QtFeatureCommon -+ QtPublicAndroidHelpers - QtPublicAppleHelpers - QtPublicCMakeHelpers - QtPublicCMakeVersionHelpers -diff --git x/qtbase/cmake/QtPlatformAndroid.cmake y/qtbase/cmake/QtPlatformAndroid.cmake -index dad58bb6a6d9b2b9c08326b9b1cac51fabe2e7a9..afd023cf75dc51779c546efdc101c67814ae9f3a 100644 ---- x/qtbase/cmake/QtPlatformAndroid.cmake -+++ y/qtbase/cmake/QtPlatformAndroid.cmake -@@ -69,17 +69,8 @@ macro(qt_internal_get_android_platform_version out_var android_platform) - string(REGEX REPLACE ".*-([0-9]+)$" "\\1" ${out_var} "${android_platform}") - endmacro() - --# Locate the highest available platform --file(GLOB android_platforms -- LIST_DIRECTORIES true -- RELATIVE "${ANDROID_SDK_ROOT}/platforms" -- "${ANDROID_SDK_ROOT}/platforms/*") --# If list is not empty --if(android_platforms) -- qt_internal_sort_android_platforms(android_platforms ${android_platforms}) -- list(REVERSE android_platforms) -- list(GET android_platforms 0 android_platform_latest) -- -+_qt_internal_detect_latest_android_platform(android_platform_latest) -+if(android_platform_latest) - qt_internal_get_android_platform_version(latest_platform_version - "${android_platform_latest}") - qt_internal_get_android_platform_version(required_platform_version -diff --git x/qtbase/cmake/QtPublicAndroidHelpers.cmake y/qtbase/cmake/QtPublicAndroidHelpers.cmake -new file mode 100644 -index 0000000000000000000000000000000000000000..dd50c011d3eb25e277e1118ea5e9686659278aee ---- /dev/null -+++ y/qtbase/cmake/QtPublicAndroidHelpers.cmake -@@ -0,0 +1,20 @@ -+# Copyright (C) 2024 The Qt Company Ltd. -+# SPDX-License-Identifier: BSD-3-Clause -+ -+function(_qt_internal_detect_latest_android_platform out_var) -+ # Locate the highest available platform -+ file(GLOB android_platforms -+ LIST_DIRECTORIES true -+ RELATIVE "${ANDROID_SDK_ROOT}/platforms" -+ "${ANDROID_SDK_ROOT}/platforms/*") -+ -+ # If list is not empty -+ if(android_platforms) -+ qt_internal_sort_android_platforms(android_platforms ${android_platforms}) -+ list(REVERSE android_platforms) -+ list(GET android_platforms 0 android_platform_latest) -+ set(${out_var} "${android_platform_latest}" PARENT_SCOPE) -+ else() -+ set(${out_var} "${out_var}-NOTFOUND" PARENT_SCOPE) -+ endif() -+endfunction() diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0008-a11y-Android-Announce-changes-to-name-description-to.patch ausweisapp2-2.4.0/libs/patches/qtbase-0008-a11y-Android-Announce-changes-to-name-description-to.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0008-a11y-Android-Announce-changes-to-name-description-to.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0008-a11y-Android-Announce-changes-to-name-description-to.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,149 @@ +From 3f810109415de7eb6eaff248805d1b7ff1ea2ef7 Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Thu, 21 Aug 2025 07:26:39 +0200 +Subject: a11y Android: Announce changes to name/description to TalkBack + +Changes are only announced for the currently focused element using the +same mechanism as a value change as Android does not offer a more +specific event type if only the contentDescription of an +AccessibilityEvent changes. + +Task-number: QTBUG-139275 +Pick-to: 6.8 6.9 6.10 +Change-Id: Ia8c0eeeb01b46a116f0e4d3e5ab1692d9492793e +Reviewed-by: Volker Hilsheimer +(cherry picked from commit cdfab6d1cd962e0bc601ee9f692840c8da871f75) +--- + .../qt/android/QtAccessibilityDelegate.java | 6 ++++++ + .../qt/android/QtAccessibilityInterface.java | 1 + + .../org/qtproject/qt/android/QtActivityDelegate.java | 5 +++++ + .../platforms/android/androidjniaccessibility.cpp | 12 ++++++++++++ + .../platforms/android/androidjniaccessibility.h | 1 + + src/plugins/platforms/android/androidjnimain.cpp | 6 ++++++ + src/plugins/platforms/android/androidjnimain.h | 1 + + .../android/qandroidplatformaccessibility.cpp | 3 +++ + 8 files changed, 35 insertions(+) + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +index 44e90eb87486deac5c2ffcd3ba097e9014d95a8f..6c0bd5d75eccfc402035e6f5139b3614185af549 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +@@ -263,6 +263,12 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + }); + } + ++ void notifyDescriptionOrNameChanged(int viewId, String value) ++ { ++ if (viewId == m_focusedVirtualViewId) ++ notifyValueChanged(viewId, value); ++ } ++ + void notifyAnnouncementEvent(int viewId, String message) + { + QtNative.runAction(() -> { +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityInterface.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityInterface.java +index 8cf08034ca4ba80d1363bfda4be2573440f13803..f7b029dd0f5a2b4570bc421dc4968dde756df112 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityInterface.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityInterface.java +@@ -10,6 +10,7 @@ interface QtAccessibilityInterface { + default void notifyObjectFocus(int viewId) { } + default void notifyScrolledEvent(int viewId) { } + default void notifyValueChanged(int viewId, String value) { } ++ default void notifyDescriptionOrNameChanged(int viewId, String value) { } + default void notifyObjectShow(int parentId) { } + default void notifyAnnouncementEvent(int viewId, String message) { } + } +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +index 15aaed7bfa93484320a37d55bacd5a56c6fb94e3..58d332c72812a0fbdf322c3be5dbd1c390e7e1b8 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +@@ -290,6 +290,11 @@ class QtActivityDelegate extends QtActivityDelegateBase + m_accessibilityDelegate.notifyValueChanged(viewId, value); + } + ++ @Override public void notifyDescriptionOrNameChanged(int viewId, String value) ++ { ++ m_accessibilityDelegate.notifyDescriptionOrNameChanged(viewId, value); ++ } ++ + @Override + public void notifyScrolledEvent(int viewId) + { +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index 64a1065f32121e1cc7466a9b7ec07cc1c8f6a1f4..88db149ed012d9ed5c037765ad8629ed25e71157 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -149,6 +149,18 @@ namespace QtAndroidAccessibility + QtAndroid::notifyValueChanged(accessibilityObjectId, value); + } + ++ // Forward declaration ++ static QString descriptionForInterface(QAccessibleInterface *iface); ++ ++ void notifyDescriptionOrNameChanged(uint accessibilityObjectId) ++ { ++ QAccessibleInterface *iface = interfaceFromId(accessibilityObjectId); ++ if (iface && iface->isValid()) { ++ const QString value = descriptionForInterface(iface); ++ QtAndroid::notifyDescriptionOrNameChanged(accessibilityObjectId, value); ++ } ++ } ++ + void notifyScrolledEvent(uint accessiblityObjectId) + { + QtAndroid::notifyScrolledEvent(accessiblityObjectId); +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.h y/qtbase/src/plugins/platforms/android/androidjniaccessibility.h +index c42a8ddccf2d9f890a662d76b3a9cb620c5e262b..9f21b7f80ead3ba777aff2f8a005e6460c44fba0 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.h ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.h +@@ -21,6 +21,7 @@ namespace QtAndroidAccessibility + void notifyObjectShow(uint accessibilityObjectId); + void notifyObjectFocus(uint accessibilityObjectId); + void notifyValueChanged(uint accessibilityObjectId); ++ void notifyDescriptionOrNameChanged(uint accessibilityObjectId); + void notifyScrolledEvent(uint accessibilityObjectId); + void notifyAnnouncementEvent(uint accessibilityObjectId, const QString &message); + void createAccessibilityContextObject(QObject *parent); +diff --git x/qtbase/src/plugins/platforms/android/androidjnimain.cpp y/qtbase/src/plugins/platforms/android/androidjnimain.cpp +index 131edc5b7a8ff4ad119d8cd98f7f58236101b2c8..e2b2c9dcd93ee1c6910a6568e8fa7f2c5648e851 100644 +--- x/qtbase/src/plugins/platforms/android/androidjnimain.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjnimain.cpp +@@ -235,6 +235,12 @@ namespace QtAndroid + "notifyValueChanged", accessibilityObjectId, value); + } + ++ void notifyDescriptionOrNameChanged(uint accessibilityObjectId, const QString &value) ++ { ++ m_backendRegister->callInterface( ++ "notifyDescriptionOrNameChanged", accessibilityObjectId, value); ++ } ++ + void notifyScrolledEvent(uint accessibilityObjectId) + { + m_backendRegister->callInterface( +diff --git x/qtbase/src/plugins/platforms/android/androidjnimain.h y/qtbase/src/plugins/platforms/android/androidjnimain.h +index 1031402d364ab8aca3a3a6b7a7704750abdd36cd..5e224f26c8053b292d98d4f3a71e992ce0752299 100644 +--- x/qtbase/src/plugins/platforms/android/androidjnimain.h ++++ y/qtbase/src/plugins/platforms/android/androidjnimain.h +@@ -60,6 +60,7 @@ namespace QtAndroid + void notifyObjectShow(uint parentObjectId); + void notifyObjectFocus(uint accessibilityObjectId); + void notifyValueChanged(uint accessibilityObjectId, jstring value); ++ void notifyDescriptionOrNameChanged(uint accessibilityObjectId, const QString &value); + void notifyScrolledEvent(uint accessibilityObjectId); + void notifyAnnouncementEvent(uint accessibilityObjectId, const QString &message); + #endif +diff --git x/qtbase/src/plugins/platforms/android/qandroidplatformaccessibility.cpp y/qtbase/src/plugins/platforms/android/qandroidplatformaccessibility.cpp +index 70067c6819e5851123a9a879afb023ff602a9180..ac8b40b9a20d089473a26f347190aac0341b23c3 100644 +--- x/qtbase/src/plugins/platforms/android/qandroidplatformaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/qandroidplatformaccessibility.cpp +@@ -36,6 +36,9 @@ void QAndroidPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent * + QtAndroidAccessibility::notifyValueChanged(event->uniqueId()); + } else if (event->type() == QAccessible::ScrollingEnd) { + QtAndroidAccessibility::notifyScrolledEvent(event->uniqueId()); ++ } else if (event->type() == QAccessible::NameChanged ++ || event->type() == QAccessible::DescriptionChanged) { ++ QtAndroidAccessibility::notifyDescriptionOrNameChanged(event->uniqueId()); + } else if (event->type() == QAccessible::Announcement) { + auto *announcementEvent = static_cast(event); + QtAndroidAccessibility::notifyAnnouncementEvent(announcementEvent->uniqueId(), diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0009-Android-Use-the-TalkBack-focus-signal-to-trigger-foc.patch ausweisapp2-2.4.0/libs/patches/qtbase-0009-Android-Use-the-TalkBack-focus-signal-to-trigger-foc.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0009-Android-Use-the-TalkBack-focus-signal-to-trigger-foc.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0009-Android-Use-the-TalkBack-focus-signal-to-trigger-foc.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,102 @@ +From 934bacb7786eb353cf74e1d58d9f7226e6f6297c Mon Sep 17 00:00:00 2001 +From: Timon Sassor +Date: Wed, 30 Jul 2025 12:44:36 +0200 +Subject: Android: Use the TalkBack focus signal to trigger focusAction + +TalkBack provides A11y specific focus signals that are emitted when +the focus frame is moved. It would be useful to pass this focus +change to the focused property of an item. + +[ChangeLog][Accessibility][Android] Use TalkBack +ACTION_ACCESSIBILITY_FOCUS to trigger focusAction. + +Fixes: QTBUG-137769 +Pick-to: 6.10 6.9 6.8 +Change-Id: I63219cfb9b8776ddaa6c85ccf1844adb1984d2b7 +Reviewed-by: Assam Boudjelthia +(cherry picked from commit e7a211d702d6aa21a30eff56ceeffc153385443e) +--- + .../qt/android/QtAccessibilityDelegate.java | 3 ++ + .../qt/android/QtNativeAccessibility.java | 1 + + .../android/androidjniaccessibility.cpp | 28 +++++++++++++++++++ + 3 files changed, 32 insertions(+) + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +index 6c0bd5d75eccfc402035e6f5139b3614185af549..bc19882840d74ed2b8a6cb40323ed73508b1a6ce 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +@@ -569,6 +569,9 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + if (success) + sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_VIEW_CLICKED); + break; ++ case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: ++ success = QtNativeAccessibility.focusAction(virtualViewId); ++ break; + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: + success = QtNativeAccessibility.scrollForward(virtualViewId); + if (success) +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java +index dd2cead8cd0c7d3c1fcd86508620749303cf2747..e63de6f79756198f1f3de1e4fc894c6a4748d2c4 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java +@@ -15,6 +15,7 @@ class QtNativeAccessibility + static native Rect screenRect(int objectId); + static native int hitTest(float x, float y); + static native boolean clickAction(int objectId); ++ static native boolean focusAction(int objectId); + static native boolean scrollForward(int objectId); + static native boolean scrollBackward(int objectId); + +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index 88db149ed012d9ed5c037765ad8629ed25e71157..a88bfc00eaf57f995465023d55560410c8f61495 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -316,6 +316,22 @@ namespace QtAndroidAccessibility + return true; + } + ++ static bool focusAction_helper(int objectId) ++ { ++ QAccessibleInterface *iface = interfaceFromId(objectId); ++ if (!iface || !iface->isValid() || !iface->actionInterface()) ++ return false; ++ ++ const auto& actionNames = iface->actionInterface()->actionNames(); ++ ++ if (actionNames.contains(QAccessibleActionInterface::setFocusAction())) { ++ invokeActionOnInterfaceInMainThread(iface->actionInterface(), ++ QAccessibleActionInterface::setFocusAction()); ++ return true; ++ } ++ return false; ++ } ++ + static jboolean clickAction(JNIEnv */*env*/, jobject /*thiz*/, jint objectId) + { + bool result = false; +@@ -327,6 +343,17 @@ namespace QtAndroidAccessibility + return result; + } + ++ static jboolean focusAction(JNIEnv */*env*/, jobject /*thiz*/, jint objectId) ++ { ++ bool result = false; ++ if (m_accessibilityContext) { ++ runInObjectContext(m_accessibilityContext, [objectId]() { ++ return focusAction_helper(objectId); ++ }, &result); ++ } ++ return result; ++ } ++ + static bool scroll_helper(int objectId, const QString &actionName) + { + QAccessibleInterface *iface = interfaceFromId(objectId); +@@ -719,6 +746,7 @@ namespace QtAndroidAccessibility + {"hitTest", "(FF)I", (void*)hitTest}, + {"populateNode", "(ILandroid/view/accessibility/AccessibilityNodeInfo;)Z", (void*)populateNode}, + {"clickAction", "(I)Z", (void*)clickAction}, ++ {"focusAction", "(I)Z", (void*)focusAction}, + {"scrollForward", "(I)Z", (void*)scrollForward}, + {"scrollBackward", "(I)Z", (void*)scrollBackward}, + }; diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0009-Android-use-latest-platform-only-if-the-default-is-n.patch ausweisapp2-2.4.0/libs/patches/qtbase-0009-Android-use-latest-platform-only-if-the-default-is-n.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0009-Android-use-latest-platform-only-if-the-default-is-n.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0009-Android-use-latest-platform-only-if-the-default-is-n.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -From 3d026a23adb452790f98b1ea5566e76c0e994314 Mon Sep 17 00:00:00 2001 -From: Assam Boudjelthia -Date: Thu, 5 Dec 2024 16:00:38 +0200 -Subject: Android: use latest platform only if the default is not found - -We initially set a value to QT_ANDROID_API_USED_FOR_JAVA as the -default supported version, so consistently use that unless it's -not found at which case try to take the latest found version. - -Task-number: QTBUG-128364 -Change-Id: I5e8404887b1649aad3ae37d2352e3ca099392935 -Reviewed-by: Lars Schmertmann -Reviewed-by: Alexey Edelev -(cherry picked from commit 953b7aaddc7579a253cd15562de6eec9476245e4) ---- - cmake/QtPlatformAndroid.cmake | 26 +++++++++++++------------- - 1 file changed, 13 insertions(+), 13 deletions(-) - -diff --git x/qtbase/cmake/QtPlatformAndroid.cmake y/qtbase/cmake/QtPlatformAndroid.cmake -index afd023cf75dc51779c546efdc101c67814ae9f3a..4819f4d2ab0662ef37fcf0adace8997b87560704 100644 ---- x/qtbase/cmake/QtPlatformAndroid.cmake -+++ y/qtbase/cmake/QtPlatformAndroid.cmake -@@ -35,9 +35,6 @@ function(qt_get_android_sdk_jar_for_api api out_jar_location) - endif() - endfunction() - --# Minimum recommend android SDK api version --set(QT_ANDROID_API_VERSION "android-34") -- - function(qt_internal_sort_android_platforms out_var) - if(CMAKE_VERSION GREATER_EQUAL 3.18) - set(platforms ${ARGN}) -@@ -65,18 +62,21 @@ function(qt_internal_sort_android_platforms out_var) - set("${out_var}" "${platforms}" PARENT_SCOPE) - endfunction() - --macro(qt_internal_get_android_platform_version out_var android_platform) -- string(REGEX REPLACE ".*-([0-9]+)$" "\\1" ${out_var} "${android_platform}") --endmacro() -+if(QT_ANDROID_API_USED_FOR_JAVA) -+ set(QT_ANDROID_API_VERSION ${QT_ANDROID_API_USED_FOR_JAVA}) -+endif() - --_qt_internal_detect_latest_android_platform(android_platform_latest) --if(android_platform_latest) -- qt_internal_get_android_platform_version(latest_platform_version -- "${android_platform_latest}") -- qt_internal_get_android_platform_version(required_platform_version -- "${QT_ANDROID_API_VERSION}") -+# Minimum recommend android SDK api version -+if(NOT QT_ANDROID_API_VERSION) -+ set(QT_ANDROID_API_VERSION "android-34") -+endif() - -- if("${latest_platform_version}" VERSION_GREATER "${required_platform_version}") -+set(jar_location "${ANDROID_SDK_ROOT}/platforms/${QT_ANDROID_API_VERSION}/android.jar") -+if(NOT EXISTS "${jar_location}") -+ _qt_internal_detect_latest_android_platform(android_platform_latest) -+ if(android_platform_latest) -+ message(NOTICE "The default platform SDK ${QT_ANDROID_API_VERSION} not found, " -+ "using the latest installed ${android_platform_latest} instead.") - set(QT_ANDROID_API_VERSION ${android_platform_latest}) - endif() - endif() diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0010-Android-Fix-freeze-on-start-when-the-activity-was-de.patch ausweisapp2-2.4.0/libs/patches/qtbase-0010-Android-Fix-freeze-on-start-when-the-activity-was-de.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0010-Android-Fix-freeze-on-start-when-the-activity-was-de.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0010-Android-Fix-freeze-on-start-when-the-activity-was-de.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ -From cb9e8979bbadcafda1d1b233a92d489b3138a122 Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Wed, 25 Dec 2024 13:41:29 +0100 -Subject: Android: Fix freeze on start when the activity was destroyed before - -This can be tested by enabling "Don't keep activities" in the developer -options. With this option Android will immediately destroy the activity -when it is moved to the background. In this case registerBackends will -be called the first time before the BackendRegister was created. Because -m_backendsRegistered was set to true even if it failed, the final call has -no effect. So we need to ensure to set m_backendsRegistered only if a -BackendRegister is available. - -Fixes: QTBUG-132085 -Pick-to: 6.9 6.8 -Change-Id: I2ea1c0e0737c982594ceb06cbaf540399c45e3f4 -Reviewed-by: Assam Boudjelthia -(cherry picked from commit 9903242ecab82c1ed72dcaf90e90a171c942a84a) ---- - .../jar/src/org/qtproject/qt/android/BackendRegister.java | 1 + - .../src/org/qtproject/qt/android/QtActivityDelegate.java | 6 +++++- - .../src/org/qtproject/qt/android/QtEmbeddedDelegate.java | 7 +++++++ - src/plugins/platforms/android/androidbackendregister.cpp | 8 +++++++- - src/plugins/platforms/android/androidbackendregister.h | 2 ++ - 5 files changed, 22 insertions(+), 2 deletions(-) - -diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/BackendRegister.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/BackendRegister.java -index b66a593ec6b79fedf5bc9e852862e8abf078e719..590c7d84aacc1879d72d9c1cdbe318b98b2913d9 100644 ---- x/qtbase/src/android/jar/src/org/qtproject/qt/android/BackendRegister.java -+++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/BackendRegister.java -@@ -4,6 +4,7 @@ package org.qtproject.qt.android; - - class BackendRegister - { -+ static native boolean isNull(); - static native void registerBackend(Class interfaceType, Object interfaceObject); - static native void unregisterBackend(Class interfaceType); - } -diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java -index 9c38bc48d3b5c9b0d20ec00c34cc85ef250c75e8..fdee51e419f4f9d3947b67eaa437f5f5ca497fef 100644 ---- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java -+++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java -@@ -59,7 +59,7 @@ class QtActivityDelegate extends QtActivityDelegateBase - - void registerBackends() - { -- if (!m_backendsRegistered) { -+ if (!m_backendsRegistered && !BackendRegister.isNull()) { - m_backendsRegistered = true; - BackendRegister.registerBackend(QtWindowInterface.class, - (QtWindowInterface)QtActivityDelegate.this); -@@ -76,6 +76,10 @@ class QtActivityDelegate extends QtActivityDelegateBase - { - if (m_backendsRegistered) { - m_backendsRegistered = false; -+ -+ if (BackendRegister.isNull()) -+ return; -+ - BackendRegister.unregisterBackend(QtWindowInterface.class); - BackendRegister.unregisterBackend(QtAccessibilityInterface.class); - BackendRegister.unregisterBackend(QtMenuInterface.class); -diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java -index 8598b91167802dd4cf9c8ded39e70a63cf716bba..0a49d9401406209c9f9bcd92231631223d711e72 100644 ---- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java -+++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java -@@ -95,12 +95,19 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase - synchronized (this) { - m_stateDetails = details; - if (details.isStarted && !m_backendsRegistered) { -+ if (BackendRegister.isNull()) -+ return; -+ - m_backendsRegistered = true; - BackendRegister.registerBackend(QtWindowInterface.class, (QtWindowInterface)this); - BackendRegister.registerBackend(QtMenuInterface.class, (QtMenuInterface)this); - BackendRegister.registerBackend(QtInputInterface.class, m_inputDelegate); - } else if (!details.isStarted && m_backendsRegistered) { - m_backendsRegistered = false; -+ -+ if (BackendRegister.isNull()) -+ return; -+ - BackendRegister.unregisterBackend(QtWindowInterface.class); - BackendRegister.unregisterBackend(QtMenuInterface.class); - BackendRegister.unregisterBackend(QtInputInterface.class); -diff --git x/qtbase/src/plugins/platforms/android/androidbackendregister.cpp y/qtbase/src/plugins/platforms/android/androidbackendregister.cpp -index bfd86138aa5090a692cf1dd58d622009153747bf..98f0ea593563b8dfcd636120b25caa710baaf7ff 100644 ---- x/qtbase/src/plugins/platforms/android/androidbackendregister.cpp -+++ y/qtbase/src/plugins/platforms/android/androidbackendregister.cpp -@@ -14,10 +14,16 @@ Q_DECLARE_JNI_CLASS(BackendRegister, "org/qtproject/qt/android/BackendRegister") - bool AndroidBackendRegister::registerNatives() - { - return QtJniTypes::BackendRegister::registerNativeMethods( -- { Q_JNI_NATIVE_SCOPED_METHOD(registerBackend, AndroidBackendRegister), -+ { Q_JNI_NATIVE_SCOPED_METHOD(isNull, AndroidBackendRegister), -+ Q_JNI_NATIVE_SCOPED_METHOD(registerBackend, AndroidBackendRegister), - Q_JNI_NATIVE_SCOPED_METHOD(unregisterBackend, AndroidBackendRegister) }); - } - -+jboolean AndroidBackendRegister::isNull(JNIEnv *, jclass) -+{ -+ return QtAndroid::backendRegister() == nullptr; -+} -+ - void AndroidBackendRegister::registerBackend(JNIEnv *, jclass, jclass interfaceClass, - jobject interface) - { -diff --git x/qtbase/src/plugins/platforms/android/androidbackendregister.h y/qtbase/src/plugins/platforms/android/androidbackendregister.h -index 502880a77f3e15867dd652502d65ac55fe5302b5..06b7b8324161cb218bc07db25814f89617e9f204 100644 ---- x/qtbase/src/plugins/platforms/android/androidbackendregister.h -+++ y/qtbase/src/plugins/platforms/android/androidbackendregister.h -@@ -60,6 +60,8 @@ private: - QMutex m_registerMutex; - QMap m_register; - -+ static jboolean isNull(JNIEnv *, jclass); -+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(isNull) - static void registerBackend(JNIEnv *, jclass, jclass interfaceClass, jobject interface); - Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(registerBackend) - static void unregisterBackend(JNIEnv *, jclass, jclass interfaceClass); diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0010-iOS-Handle-Z-gesture-to-request-closing.patch ausweisapp2-2.4.0/libs/patches/qtbase-0010-iOS-Handle-Z-gesture-to-request-closing.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0010-iOS-Handle-Z-gesture-to-request-closing.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0010-iOS-Handle-Z-gesture-to-request-closing.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,37 @@ +From 3840acd7d90652cc6744767ef29d4d9442755f7a Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Thu, 21 Aug 2025 14:42:14 +0200 +Subject: iOS: Handle Z gesture to request closing + +This is similiar to the implementation for Android in +QGuiApplicationPrivate::processKeyEvent and allows to +implement onClosing in ApplicationWindow to decide if +the application should really be closed or only a view +should be popped to execute a "back". + +Pick-to: 6.10 6.9 6.8 +Fixes: QTBUG-139363 +Change-Id: Ifd3d830f62043ee1fae928c3d7421a973ca07f78 +--- + src/plugins/platforms/ios/quiview_accessibility.mm | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git x/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm y/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm +index 9eef855b620d01b83c34906112498a8eeecac03d..3858c2ca5374b30bf11f0fd4572d79afa0c9c055 100644 +--- x/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm ++++ y/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm +@@ -87,4 +87,14 @@ + return m_accessibleElements; + } + ++- (BOOL)accessibilityPerformEscape { ++ QWindow *win = QGuiApplication::focusWindow(); ++ if (win) { ++ QWindowSystemInterface::handleCloseEvent(win); ++ return YES; ++ } ++ ++ return NO; ++} ++ + @end diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0011-Check-for-valid-QAccessibleInterface-before-invoking.patch ausweisapp2-2.4.0/libs/patches/qtbase-0011-Check-for-valid-QAccessibleInterface-before-invoking.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0011-Check-for-valid-QAccessibleInterface-before-invoking.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0011-Check-for-valid-QAccessibleInterface-before-invoking.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,131 +0,0 @@ -From ef6320938ced9729ade5f3ef91846e4ce36892e7 Mon Sep 17 00:00:00 2001 -From: Jan Moeller -Date: Mon, 9 Dec 2024 16:13:08 +0100 -Subject: Check for valid QAccessibleInterface before invoking action -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -To avoid illegal access, the change ensures that the QAccessibleInterface -is non-null and valid before accessing its QAccessibleActionInterface. - -The check for the validity was also added to existing code which -previously only checked for the QAccessibleInterface not being null. - -Fixes: QTBUG-132059 -Pick-to: 6.8 6.9 -Change-Id: I69fc4f9bb052ded8f188032d324666d0c00b9c3c -Reviewed-by: Michael Weghorn -Reviewed-by: Lars Schmertmann -Reviewed-by: Tor Arne Vestbø -(cherry picked from commit 279c891ddf0ad10dd86c8fc836ce385df57593c4) ---- - .../platforms/ios/quiaccessibilityelement.mm | 32 +++++++++++++++---- - 1 file changed, 26 insertions(+), 6 deletions(-) - -diff --git x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm -index fa54f61967eae27de6ac4531a724ae4060f86910..754ad0d9c01d2818e64a33ab5097be28d3e71c9e 100644 ---- x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm -+++ y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm -@@ -64,7 +64,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); - - (NSString*)accessibilityLabel - { - QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); -- if (!iface) { -+ if (!iface || !iface->isValid()) { - qWarning() << "invalid accessible interface for: " << self.axid; - return @""; - } -@@ -76,7 +76,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); - - (NSString*)accessibilityIdentifier - { - QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); -- if (!iface) { -+ if (!iface || !iface->isValid()) { - qWarning() << "invalid accessible interface for: " << self.axid; - return @""; - } -@@ -86,7 +86,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); - - (NSString*)accessibilityHint - { - QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); -- if (!iface) { -+ if (!iface || !iface->isValid()) { - qWarning() << "invalid accessible interface for: " << self.axid; - return @""; - } -@@ -96,7 +96,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); - - (NSString*)accessibilityValue - { - QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); -- if (!iface) { -+ if (!iface || !iface->isValid()) { - qWarning() << "invalid accessible interface for: " << self.axid; - return @""; - } -@@ -121,7 +121,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); - - (CGRect)accessibilityFrame - { - QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); -- if (!iface) { -+ if (!iface || !iface->isValid()) { - qWarning() << "invalid accessible interface for: " << self.axid; - return CGRect(); - } -@@ -135,7 +135,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); - UIAccessibilityTraits traits = UIAccessibilityTraitNone; - - QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); -- if (!iface) { -+ if (!iface || !iface->isValid()) { - qWarning() << "invalid accessible interface for: " << self.axid; - return traits; - } -@@ -177,6 +177,11 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); - - (BOOL)accessibilityActivate - { - QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); -+ if (!iface || !iface->isValid()) { -+ qWarning() << "invalid accessible interface for: " << self.axid; -+ return NO; -+ } -+ - if (QAccessibleActionInterface *action = iface->actionInterface()) { - if (action->actionNames().contains(QAccessibleActionInterface::pressAction())) { - action->doAction(QAccessibleActionInterface::pressAction()); -@@ -192,6 +197,11 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); - - (void)accessibilityIncrement - { - QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); -+ if (!iface || !iface->isValid()) { -+ qWarning() << "invalid accessible interface for: " << self.axid; -+ return; -+ } -+ - if (QAccessibleActionInterface *action = iface->actionInterface()) - action->doAction(QAccessibleActionInterface::increaseAction()); - } -@@ -199,6 +209,11 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); - - (void)accessibilityDecrement - { - QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); -+ if (!iface || !iface->isValid()) { -+ qWarning() << "invalid accessible interface for: " << self.axid; -+ return; -+ } -+ - if (QAccessibleActionInterface *action = iface->actionInterface()) - action->doAction(QAccessibleActionInterface::decreaseAction()); - } -@@ -206,6 +221,11 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); - - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction - { - QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); -+ if (!iface || !iface->isValid()) { -+ qWarning() << "invalid accessible interface for: " << self.axid; -+ return NO; -+ } -+ - QAccessibleActionInterface *action = iface->actionInterface(); - if (!action) - return NO; diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0011-a11y-Windows-Announce-change-to-Accessible.name-for-.patch ausweisapp2-2.4.0/libs/patches/qtbase-0011-a11y-Windows-Announce-change-to-Accessible.name-for-.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0011-a11y-Windows-Announce-change-to-Accessible.name-for-.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0011-a11y-Windows-Announce-change-to-Accessible.name-for-.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,37 @@ +From 424a5a02075b01c36e4068f7afbcf061fbfc50cd Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Fri, 22 Aug 2025 10:29:43 +0200 +Subject: a11y Windows: Announce change to Accessible.name for focused element + +The changeset bcd0a3220348778c0d72faac202efb97f4d6dd07 restricted +notifications of updates to Accessible.name to the role +QAccessible::ComboBox to avoid slowdowns to the application. +This change extends the notification for the currently focused element +so the user is correctly informed. + +Task-number: QTBUG-139275 +Pick-to: 6.8 6.9 6.10 +Change-Id: Ic0daa0b10407780434594b9b5888c9d740c4b80b +Reviewed-by: Volker Hilsheimer +(cherry picked from commit 4161ae0a75acbec8a961b8ef5611acd6d7fc9b16) +--- + .../windows/uiautomation/qwindowsuiamainprovider.cpp | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +index 30471ee68c1eb7868edd21154fd8e35126e8f199..3417a3892115ddf303e612ab5bca62aa1d32dada 100644 +--- x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp ++++ y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +@@ -160,9 +160,9 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve + void QWindowsUiaMainProvider::notifyNameChange(QAccessibleEvent *event) + { + if (QAccessibleInterface *accessible = event->accessibleInterface()) { +- // Restrict notification to combo boxes, which need it for accessibility, +- // in order to avoid slowdowns with unnecessary notifications. +- if (accessible->role() == QAccessible::ComboBox) { ++ // Restrict notification to combo boxes and the currently focused element, which ++ // need it for accessibility, in order to avoid slowdowns with unnecessary notifications. ++ if (accessible->role() == QAccessible::ComboBox || accessible->state().focused) { + if (auto provider = providerForAccessible(accessible)) { + QComVariant oldVal; + QComVariant newVal{ accessible->text(QAccessible::Name) }; diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0012-Fix-broadcasts-with-dual-stack-IPv6.patch ausweisapp2-2.4.0/libs/patches/qtbase-0012-Fix-broadcasts-with-dual-stack-IPv6.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0012-Fix-broadcasts-with-dual-stack-IPv6.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0012-Fix-broadcasts-with-dual-stack-IPv6.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,30 @@ +From 5f39800dd2bdc189f422431dbddc994974dc5057 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= +Date: Tue, 26 Aug 2025 15:47:33 +0200 +Subject: Fix broadcasts with dual-stack IPv6 + +This amends 55d58e1d5a936ef3daa970665423a50a4f80118b. + +Fixes: QTBUG-139586 +Task-number: QTBUG-130070 +Pick-to: 6.10 6.9 6.8 6.5 +Change-Id: I4ddb9327e066415e5922aeaa7add7655ca2b17ff +Reviewed-by: Thiago Macieira +(cherry picked from commit daeb703dea77cb20e02f24c5f76daeb0fbda5c68) +--- + src/network/socket/qnativesocketengine.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git x/qtbase/src/network/socket/qnativesocketengine.cpp y/qtbase/src/network/socket/qnativesocketengine.cpp +index 14fc7c9c3f7329fbfe3d418991dbf7fa72baeafb..ca6281f85dbfdb780954970dbb9fdab2028bb5b4 100644 +--- x/qtbase/src/network/socket/qnativesocketengine.cpp ++++ y/qtbase/src/network/socket/qnativesocketengine.cpp +@@ -431,7 +431,7 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb + if (socketType == QAbstractSocket::UdpSocket) { + // Set the broadcasting flag if it's a UDP socket. + // IPv6 does not support broadcast — only set option for IPv4 +- if (protocol == QAbstractSocket::IPv4Protocol) { ++ if (protocol != QAbstractSocket::IPv6Protocol) { + if (!setOption(BroadcastSocketOption, 1)) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::BroadcastingInitFailedErrorString); diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0012-Fix-usage-of-API-functions-on-Windows-1607-Build-143.patch ausweisapp2-2.4.0/libs/patches/qtbase-0012-Fix-usage-of-API-functions-on-Windows-1607-Build-143.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0012-Fix-usage-of-API-functions-on-Windows-1607-Build-143.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0012-Fix-usage-of-API-functions-on-Windows-1607-Build-143.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,101 +0,0 @@ -From a6863a9528302010b85773ad16cabb117313ef08 Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Thu, 13 Mar 2025 12:55:05 +0100 -Subject: Fix usage of API functions on Windows 1607 (Build 14393) - -* Windows Server 2016, Windows 10 LTSB 2016 and Windows 10 version 1607: - SetThreadDescription is only available by Run Time Dynamic Linking in - KernelBase.dll. See [1]. -* According to [2] UiaRaiseNotificationEvent should be available on - Windows Server 2016 but in fact it is not. - -[1] https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreaddescription -[2] https://learn.microsoft.com/de-de/windows/win32/api/uiautomationcoreapi/nf-uiautomationcoreapi-uiaraisenotificationevent - -Fixes: QTBUG-134075 -Pick-to: 6.8 6.8.3 6.9 6.9.0 -Change-Id: I3c4c733a4112a72b75f91f017a278dff2454e100 -Reviewed-by: Thiago Macieira -(cherry picked from commit a9e251332cbec0f64c9c085349836af7276c55d3) ---- - src/corelib/thread/qthread_win.cpp | 14 ++++++++++++++ - .../uiautomation/qwindowsuiamainprovider.cpp | 16 +++++++++++++++- - .../uiautomation/qwindowsuiautomation.cpp | 4 ++-- - 3 files changed, 31 insertions(+), 3 deletions(-) - -diff --git x/qtbase/src/corelib/thread/qthread_win.cpp y/qtbase/src/corelib/thread/qthread_win.cpp -index 33dd03166cf1f6639a9e805f16aeba454f1fe5d9..0dd3889d6793751935f279bedacc02bc35ccddb9 100644 ---- x/qtbase/src/corelib/thread/qthread_win.cpp -+++ y/qtbase/src/corelib/thread/qthread_win.cpp -@@ -263,7 +263,21 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi - QString threadName = std::exchange(thr->d_func()->objectName, {}); - if (Q_LIKELY(threadName.isEmpty())) - threadName = QString::fromUtf8(thr->metaObject()->className()); -+#ifndef QT_WIN_SERVER_2016_COMPAT - SetThreadDescription(GetCurrentThread(), reinterpret_cast(threadName.utf16())); -+#else -+ HMODULE kernelbase = GetModuleHandleW(L"kernelbase.dll"); -+ if (kernelbase != NULL) { -+ typedef HRESULT (WINAPI *DESCFUNC)(HANDLE, PCWSTR); -+ -+ DESCFUNC setThreadDescription = -+ (DESCFUNC)GetProcAddress(kernelbase, "SetThreadDescription"); -+ if (setThreadDescription != NULL) { -+ setThreadDescription(GetCurrentThread(), -+ reinterpret_cast(threadName.utf16())); -+ } -+ } -+#endif - - emit thr->started(QThread::QPrivateSignal()); - QThread::setTerminationEnabled(true); -diff --git x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp -index 56181caa215456e7fa31d490c1f76803eab537a2..0000e9d0a1e626e04f6248e26028d9ccce6cb664 100644 ---- x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp -+++ y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp -@@ -212,9 +212,23 @@ void QWindowsUiaMainProvider::raiseNotification(QAccessibleAnnouncementEvent *ev - ? NotificationProcessing_ImportantAll - : NotificationProcessing_All; - QBStr activityId{ QString::fromLatin1("") }; -+#if !defined(Q_CC_MSVC) || !defined(QT_WIN_SERVER_2016_COMPAT) - UiaRaiseNotificationEvent(provider.Get(), NotificationKind_Other, processing, message.bstr(), - activityId.bstr()); -- -+#else -+ HMODULE uiautomationcore = GetModuleHandleW(L"UIAutomationCore.dll"); -+ if (uiautomationcore != NULL) { -+ typedef HRESULT (WINAPI *EVENTFUNC)(IRawElementProviderSimple *, NotificationKind, -+ NotificationProcessing, BSTR, BSTR); -+ -+ EVENTFUNC uiaRaiseNotificationEvent = -+ (EVENTFUNC)GetProcAddress(uiautomationcore, "UiaRaiseNotificationEvent"); -+ if (uiaRaiseNotificationEvent != NULL) { -+ uiaRaiseNotificationEvent(provider.Get(), NotificationKind_Other, processing, -+ message.bstr(), activityId.bstr()); -+ } -+ } -+#endif - } - } - } -diff --git x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp -index 6954a881d09d4eb2c8f5ffb80d29d263e9215aca..144ad36f00e89621e04ffc10c343e0759b6c4192 100644 ---- x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp -+++ y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp -@@ -6,7 +6,7 @@ - - #include "qwindowsuiautomation.h" - --#if defined(__MINGW32__) || defined(__MINGW64__) -+#ifndef Q_CC_MSVC - - template - struct winapi_func -@@ -77,6 +77,6 @@ HRESULT WINAPI UiaRaiseNotificationEvent( - return func.invoke(pProvider, notificationKind, notificationProcessing, displayString, activityId); - } - --#endif // defined(__MINGW32__) || defined(__MINGW64__) -+#endif // !Q_CC_MSVC - - #endif // QT_CONFIG(accessibility) diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0013-a11y-iOS-Inform-VoiceOver-about-used-language.patch ausweisapp2-2.4.0/libs/patches/qtbase-0013-a11y-iOS-Inform-VoiceOver-about-used-language.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0013-a11y-iOS-Inform-VoiceOver-about-used-language.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0013-a11y-iOS-Inform-VoiceOver-about-used-language.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,40 @@ +From a5018606c77ef32ac1df3412712804266016733d Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Thu, 28 Aug 2025 11:31:22 +0200 +Subject: a11y iOS: Inform VoiceOver about used language + +The property accessibilityLanguage is used by VoiceOver on iOS and +allows to tell the screen reader which language package to use. This +is helpful if the app's language is different from the OS language. +Without this change english texts are read with a german accent if +the OS language is set to German and the app language to English. + +Users will have to set QLocale::setDefault to their desired app +language in order for this behavior to work. + +On macOS VoiceOver does not evaluate this property and texts are +always read with the OS language. + +Pick-to: 6.8 6.10 +Change-Id: I469652ff44f9d788ce6e6bdfa6c34d1570f2e439 +--- + src/plugins/platforms/ios/quiaccessibilityelement.mm | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +index 33321a626efed321d3cb5cacc17e6bce452233e9..12f519eaa9ec846319854b4beb9eb5d9895db6a7 100644 +--- x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm ++++ y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +@@ -259,6 +259,12 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + return NO; + } + ++- (NSString*)accessibilityLanguage ++{ ++ const QLocale locale; ++ return locale.bcp47Name().toNSString(); ++} ++ + @end + + #endif diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0014-a11y-Android-Inform-TalkBack-about-used-language.patch ausweisapp2-2.4.0/libs/patches/qtbase-0014-a11y-Android-Inform-TalkBack-about-used-language.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0014-a11y-Android-Inform-TalkBack-about-used-language.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0014-a11y-Android-Inform-TalkBack-about-used-language.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,124 @@ +From 6450b6e320b978d8e49c64aeb56f227bb60f5bfa Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Mon, 1 Sep 2025 13:15:14 +0200 +Subject: a11y Android: Inform TalkBack about used language + +This is helpful if the app's language is different from the OS language. +Without this change english texts are read with a german accent if +the OS language is set to German and the app language to English. + +Users will have to set QLocale::setDefault to their desired app +language in order for this behavior to work. + +Pick-to: 6.8 6.10 +Change-Id: Id4bf0280469229c202ab3cf2480903212d351c2b +--- + .../qt/android/QtAccessibilityDelegate.java | 21 +++++++++++++++++-- + .../qt/android/QtNativeAccessibility.java | 1 + + .../android/androidjniaccessibility.cpp | 7 +++++++ + 3 files changed, 27 insertions(+), 2 deletions(-) + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +index bc19882840d74ed2b8a6cb40323ed73508b1a6ce..7f64928b6cd14517ecf60b63ec2a29eaec2fce4b 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +@@ -10,6 +10,10 @@ import android.os.Build; + import android.os.Bundle; + import android.system.Os; + import android.text.TextUtils; ++import android.text.SpannableString; ++import android.text.Spanned; ++import android.text.style.LocaleSpan; ++import java.util.Locale; + import android.util.Log; + import android.view.MotionEvent; + import android.view.View; +@@ -166,6 +170,15 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + return true; + } + ++ SpannableString addLocaleSpan(String value) ++ { ++ SpannableString localeValue = new SpannableString(value); ++ LocaleSpan localeSpan = ++ new LocaleSpan(Locale.forLanguageTag(QtNativeAccessibility.appLanguage())); ++ localeValue.setSpan(localeSpan, 0, localeValue.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ++ return localeValue; ++ } ++ + void notifyScrolledEvent(int viewId) + { + QtNative.runAction(() -> sendEventForVirtualViewId(viewId, +@@ -248,7 +261,7 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + event.setEnabled(true); + event.setClassName(getNodeForVirtualViewId(viewId).getClassName()); + +- event.setContentDescription(value); ++ event.setContentDescription(addLocaleSpan(value)); + + if (event.getText().isEmpty() && TextUtils.isEmpty(event.getContentDescription())) { + Log.w(TAG, "No value to announce for " + event.getClassName()); +@@ -355,7 +368,9 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + event.setEnabled(true); + event.setClassName(getNodeForVirtualViewId(virtualViewId).getClassName()); + +- event.setContentDescription(QtNativeAccessibility.descriptionForAccessibleObject(virtualViewId)); ++ String description = QtNativeAccessibility.descriptionForAccessibleObject(virtualViewId); ++ event.setContentDescription(addLocaleSpan(description)); ++ + if (event.getText().isEmpty() && TextUtils.isEmpty(event.getContentDescription())) + Log.w(TAG, "AccessibilityEvent with empty description"); + +@@ -458,6 +473,8 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + // set only if valid, otherwise we return a node that is invalid and will crash when accessed + node.setSource(m_view, virtualViewId); + ++ node.setContentDescription(addLocaleSpan(node.getContentDescription().toString())); ++ + if (TextUtils.isEmpty(node.getText()) && TextUtils.isEmpty(node.getContentDescription())) + Log.w(TAG, "AccessibilityNodeInfo with empty contentDescription: " + virtualViewId); + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java +index e63de6f79756198f1f3de1e4fc894c6a4748d2c4..cfef859ab05d90a38de53c0a53a8dc2d4a1e4967 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java +@@ -12,6 +12,7 @@ class QtNativeAccessibility + static native int[] childIdListForAccessibleObject(int objectId); + static native int parentId(int objectId); + static native String descriptionForAccessibleObject(int objectId); ++ static native String appLanguage(); + static native Rect screenRect(int objectId); + static native int hitTest(float x, float y); + static native boolean clickAction(int objectId); +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index a88bfc00eaf57f995465023d55560410c8f61495..8de9758bb0f7a1f7c27c3cd1081a2074f8d10738 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + + static const char m_qtTag[] = "Qt A11Y"; + +@@ -638,6 +639,11 @@ namespace QtAndroidAccessibility + return env->NewString((jchar*) desc.constData(), (jsize) desc.size()); + } + ++ static jstring appLanguage(JNIEnv *env, jobject /*thiz*/) ++ { ++ const QString localeName = QLocale().bcp47Name(); ++ return env->NewString((jchar *)localeName.constData(), (jsize)localeName.size()); ++ } + + struct NodeInfo + { +@@ -742,6 +748,7 @@ namespace QtAndroidAccessibility + {"childIdListForAccessibleObject", "(I)[I", (jintArray)childIdListForAccessibleObject}, + {"parentId", "(I)I", (void*)parentId}, + {"descriptionForAccessibleObject", "(I)Ljava/lang/String;", (jstring)descriptionForAccessibleObject}, ++ {"appLanguage", "()Ljava/lang/String;", (jstring)appLanguage}, + {"screenRect", "(I)Landroid/graphics/Rect;", (jobject)screenRect}, + {"hitTest", "(FF)I", (void*)hitTest}, + {"populateNode", "(ILandroid/view/accessibility/AccessibilityNodeInfo;)Z", (void*)populateNode}, diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0015-iOS-Fix-a11y-representation-of-RadioButton-and-Check.patch ausweisapp2-2.4.0/libs/patches/qtbase-0015-iOS-Fix-a11y-representation-of-RadioButton-and-Check.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0015-iOS-Fix-a11y-representation-of-RadioButton-and-Check.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0015-iOS-Fix-a11y-representation-of-RadioButton-and-Check.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,58 @@ +From 6be8d77fd769706549557ec0c585ab2357fea984 Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Wed, 10 Sep 2025 15:08:11 +0200 +Subject: iOS: Fix a11y representation of RadioButton and CheckBox +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +RadioButton and CheckBox should not provide a value because this +leads to the behavior that iOS read out both states (checked and +unchecked). Also we need to provide own strings in multiple +languages because resolving 0 and 1 is not supported by iOS in +this case. This behavior is not intended by iOS. Instead we need +to provide the state with a UIAccessibilityTrait. This creates +the same representation as for the items in the iOS settings and +uses the iOS specific wording. + +Task-number: QTBUG-139676 +Pick-to: 6.8 6.9 6.10 +Change-Id: I1369291cc9e8fbbe9ae65f2ca6c742d942d2acd6 +Reviewed-by: Tor Arne Vestbø +(cherry picked from commit e9da800428045f3dc4542f8767bb4b3335a3df48) +--- + src/plugins/platforms/ios/quiaccessibilityelement.mm | 11 ++++++++++- + 1 file changed, 10 insertions(+), 1 deletion(-) + +diff --git x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +index 12f519eaa9ec846319854b4beb9eb5d9895db6a7..882c60dadab6d8c53c651d93f76d9f50eac3a958 100644 +--- x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm ++++ y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +@@ -109,10 +109,15 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + + QAccessible::State state = iface->state(); + +- if (state.checkable) ++ if (state.checkable) { ++ if (iface->role() == QAccessible::CheckBox ++ || iface->role() == QAccessible::RadioButton) ++ return @""; ++ + return state.checked + ? QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_CHECKED).toNSString() + : QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_UNCHECKED).toNSString(); ++ } + + QAccessibleValueInterface *val = iface->valueInterface(); + if (val) { +@@ -159,6 +164,10 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + const auto accessibleRole = iface->role(); + if (accessibleRole == QAccessible::Button) { + traits |= UIAccessibilityTraitButton; ++ } else if (accessibleRole == QAccessible::CheckBox ++ || accessibleRole == QAccessible::RadioButton) { ++ if (state.checked) ++ traits |= UIAccessibilityTraitSelected; + } else if (accessibleRole == QAccessible::EditableText) { + static auto defaultTextFieldTraits = []{ + auto *textField = [[[UITextField alloc] initWithFrame:CGRectZero] autorelease]; diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0016-a11y-Add-Switch-role.patch ausweisapp2-2.4.0/libs/patches/qtbase-0016-a11y-Add-Switch-role.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0016-a11y-Add-Switch-role.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0016-a11y-Add-Switch-role.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,255 @@ +From 7db2623ecafad5d68d908a35289bb9448109a0c8 Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Thu, 25 Sep 2025 11:45:26 +0200 +Subject: a11y: Add Switch role + +Distinguishing a Switch from a CheckBox is necessary on at least Android. +Windows uses the already available IToggleProvider interface, which gets +correctly recognized as a switch when used with UIA_ButtonControlTypeId. + +[ChangeLog][QtGui][QAccessible] Added new Switch role that can be used +instead of CheckBox for more accurately communicating the elements role +to assistive technology. + +Fixes: QTBUG-139676 +Change-Id: I77d33ea5ad32e58ba01c63c418c11ccc25d19c54 +Reviewed-by: Michael Weghorn +Reviewed-by: Volker Hilsheimer +(cherry picked from commit 293032ab2f4ad7bad0ae4dc9384fbcf448784d6f) +--- + .../accessible/linux/qspiaccessiblebridge.cpp | 6 ++++ + src/gui/accessible/qaccessible.cpp | 1 + + src/gui/accessible/qaccessible_base.h | 1 + + .../android/androidjniaccessibility.cpp | 3 +- + .../platforms/cocoa/qcocoaaccessibility.mm | 8 ++++- + .../platforms/ios/quiaccessibilityelement.mm | 9 +++++ + .../platforms/wasm/qwasmaccessibility.cpp | 36 +++++++++++++++++++ + .../platforms/wasm/qwasmaccessibility.h | 1 + + .../uiautomation/qwindowsuiamainprovider.cpp | 5 +-- + .../windows/uiautomation/qwindowsuiautils.cpp | 1 + + 10 files changed, 67 insertions(+), 4 deletions(-) + +diff --git x/qtbase/src/gui/accessible/linux/qspiaccessiblebridge.cpp y/qtbase/src/gui/accessible/linux/qspiaccessiblebridge.cpp +index 1eab8846653b9a3ca87dfe76e347245932bbc45f..861c73078f6f94d6d703b05c040bc7a6fcdd5384 100644 +--- x/qtbase/src/gui/accessible/linux/qspiaccessiblebridge.cpp ++++ y/qtbase/src/gui/accessible/linux/qspiaccessiblebridge.cpp +@@ -183,6 +183,12 @@ static RoleMapping map[] = { + //: Role of an accessible object + { QAccessible::CheckBox, ATSPI_ROLE_CHECK_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "check box") }, + //: Role of an accessible object ++#if ATSPI_ROLE_COUNT >= 132 ++ { QAccessible::Switch, ATSPI_ROLE_SWITCH, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "switch") }, ++#else ++ { QAccessible::Switch, ATSPI_ROLE_CHECK_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "check box") }, ++#endif ++ //: Role of an accessible object + { QAccessible::RadioButton, ATSPI_ROLE_RADIO_BUTTON, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "radio button") }, + //: Role of an accessible object + { QAccessible::ComboBox, ATSPI_ROLE_COMBO_BOX, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "combo box") }, +diff --git x/qtbase/src/gui/accessible/qaccessible.cpp y/qtbase/src/gui/accessible/qaccessible.cpp +index 510931b4760e72669972e46061dec2ea145909f8..a2fcb8a1be73f313d11db0b9de08167f8296e691 100644 +--- x/qtbase/src/gui/accessible/qaccessible.cpp ++++ y/qtbase/src/gui/accessible/qaccessible.cpp +@@ -341,6 +341,7 @@ Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core"); + \value Splitter A splitter distributing available space between its child widgets. + \value StaticText Static text, such as labels for other widgets. + \value StatusBar A status bar. ++ \value [since 6.11] Switch A switch that can be toggled on or off. + \value Table A table representing data in a grid of rows and columns. + \value Terminal A terminal or command line interface. + \value TitleBar The title bar caption of a window. +diff --git x/qtbase/src/gui/accessible/qaccessible_base.h y/qtbase/src/gui/accessible/qaccessible_base.h +index 27a0fd4f92efde4b3589e5a7ba39de64b7f1d317..c4697b468de9f74202dbed24d896c173f3bad165 100644 +--- x/qtbase/src/gui/accessible/qaccessible_base.h ++++ y/qtbase/src/gui/accessible/qaccessible_base.h +@@ -266,6 +266,7 @@ public: + WebDocument = 0x00000084, + Section = 0x00000085, + Notification = 0x00000086, ++ Switch = 0x00000087, + + // IAccessible2 roles + // IA2_ROLE_CANVAS = 0x401, // An object that can be drawn into and to manage events from the objects drawn into it +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index 8de9758bb0f7a1f7c27c3cd1081a2074f8d10738..4222235d05a613093dded9c63ee96a2628a82494 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -475,7 +475,6 @@ namespace QtAndroidAccessibility + case QAccessible::Role::Link: + { + if (state.checkable) +- // There is also a android.widget.Switch for which we have no match. + return QStringLiteral("android.widget.ToggleButton"); + return QStringLiteral("android.widget.Button"); + } +@@ -483,6 +482,8 @@ namespace QtAndroidAccessibility + // As of android/accessibility/utils/Role.java::getRole a CheckBox + // is NOT android.widget.CheckBox + return QStringLiteral("android.widget.CompoundButton"); ++ case QAccessible::Role::Switch: ++ return QStringLiteral("android.widget.Switch"); + case QAccessible::Role::Clock: + return QStringLiteral("android.widget.TextClock"); + case QAccessible::Role::ComboBox: +diff --git x/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm y/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +index d77035a776aa5dc7ac258a0147ffede4d6587920..6063795d6b4e9f9ee9d2ad0d431d84e49abd4b75 100644 +--- x/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm ++++ y/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +@@ -131,6 +131,7 @@ static void populateRoleMap() + roleMap[QAccessible::ComboBox] = NSAccessibilityComboBoxRole; + roleMap[QAccessible::RadioButton] = NSAccessibilityRadioButtonRole; + roleMap[QAccessible::CheckBox] = NSAccessibilityCheckBoxRole; ++ roleMap[QAccessible::Switch] = NSAccessibilityCheckBoxRole; + roleMap[QAccessible::StaticText] = NSAccessibilityStaticTextRole; + roleMap[QAccessible::Table] = NSAccessibilityTableRole; + roleMap[QAccessible::StatusBar] = NSAccessibilityStaticTextRole; +@@ -203,6 +204,8 @@ NSString *macSubrole(QAccessibleInterface *interface) + return NSAccessibilitySecureTextFieldSubrole; + if (interface->role() == QAccessible::PageTab) + return NSAccessibilityTabButtonSubrole; ++ if (interface->role() == QAccessible::Switch) ++ return NSAccessibilitySwitchSubrole; + return nil; + } + +@@ -327,8 +330,11 @@ NSString *getTranslatedAction(const QString &qtAction) + QString translateAction(NSString *nsAction, QAccessibleInterface *interface) + { + if ([nsAction compare: NSAccessibilityPressAction] == NSOrderedSame) { +- if (interface->role() == QAccessible::CheckBox || interface->role() == QAccessible::RadioButton) ++ if (interface->role() == QAccessible::CheckBox ++ || interface->role() == QAccessible::RadioButton ++ || interface->role() == QAccessible::Switch) { + return QAccessibleActionInterface::toggleAction(); ++ } + return QAccessibleActionInterface::pressAction(); + } else if ([nsAction compare: NSAccessibilityIncrementAction] == NSOrderedSame) + return QAccessibleActionInterface::increaseAction(); +diff --git x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +index 882c60dadab6d8c53c651d93f76d9f50eac3a958..d388f2ab33bbb83ee2502acb2a55f277d081a568 100644 +--- x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm ++++ y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +@@ -114,6 +114,9 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + || iface->role() == QAccessible::RadioButton) + return @""; + ++ if (iface->role() == QAccessible::Switch) ++ return state.checked ? @"1" : @"0"; ++ + return state.checked + ? QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_CHECKED).toNSString() + : QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_UNCHECKED).toNSString(); +@@ -168,6 +171,12 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + || accessibleRole == QAccessible::RadioButton) { + if (state.checked) + traits |= UIAccessibilityTraitSelected; ++ } else if (accessibleRole == QAccessible::Switch) { ++ if (@available(iOS 17.0, *)) { ++ traits |= UIAccessibilityTraitToggleButton; ++ } else { ++ traits |= UIAccessibilityTraitButton; ++ } + } else if (accessibleRole == QAccessible::EditableText) { + static auto defaultTextFieldTraits = []{ + auto *textField = [[[UITextField alloc] initWithFrame:CGRectZero] autorelease]; +diff --git x/qtbase/src/plugins/platforms/wasm/qwasmaccessibility.cpp y/qtbase/src/plugins/platforms/wasm/qwasmaccessibility.cpp +index 2e430176bec2b124a1ff6ee01dbf7333d2ccc1ec..b51c4e100401efc8812691fb096ed5a7522400a7 100644 +--- x/qtbase/src/plugins/platforms/wasm/qwasmaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/wasm/qwasmaccessibility.cpp +@@ -168,6 +168,17 @@ emscripten::val QWasmAccessibility::createHtmlElement(QAccessibleInterface *ifac + + } break; + ++ case QAccessible::Switch: { ++ element = document.call("createElement", std::string("button")); ++ setAttribute(element, "type", "button"); ++ setAttribute(element, "role", "switch"); ++ if (iface->state().checked) ++ setAttribute(element, "aria-checked", "true"); ++ else ++ setAttribute(element, "aria-checked", "false"); ++ addEventListener(element, "change"); ++ } break; ++ + case QAccessible::RadioButton: { + element = document.call("createElement", std::string("input")); + element.call("setAttribute", std::string("type"), std::string("radio")); +@@ -483,6 +494,28 @@ void QWasmAccessibility::handleCheckBoxUpdate(QAccessibleEvent *event) + break; + } + } ++ ++void QWasmAccessibility::handleSwitchUpdate(QAccessibleEvent *event) ++{ ++ switch (event->type()) { ++ case QAccessible::Focus: ++ case QAccessible::NameChanged: { ++ setHtmlElementTextName(event->accessibleInterface()); ++ } break; ++ case QAccessible::StateChanged: { ++ QAccessibleInterface *accessible = event->accessibleInterface(); ++ const emscripten::val element = getHtmlElement(accessible); ++ if (accessible->state().checked) ++ setAttribute(element, "aria-checked", "true"); ++ else ++ setAttribute(element, "aria-checked", "false"); ++ } break; ++ default: ++ qCDebug(lcQpaAccessibility) << "TODO: implement handleSwitchUpdate for event" << event->type(); ++ break; ++ } ++} ++ + void QWasmAccessibility::handleToolUpdate(QAccessibleEvent *event) + { + QAccessibleInterface *iface = event->accessibleInterface(); +@@ -729,6 +762,9 @@ void QWasmAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) + case QAccessible::CheckBox: + handleCheckBoxUpdate(event); + break; ++ case QAccessible::Switch: ++ handleSwitchUpdate(event); ++ break; + case QAccessible::EditableText: + handleLineEditUpdate(event); + break; +diff --git x/qtbase/src/plugins/platforms/wasm/qwasmaccessibility.h y/qtbase/src/plugins/platforms/wasm/qwasmaccessibility.h +index c4be7f0d72a14143635d0f70d595498dc0bec3b0..71d2794f2eab6c97eee8aa2995a849f7e4f747c3 100644 +--- x/qtbase/src/plugins/platforms/wasm/qwasmaccessibility.h ++++ y/qtbase/src/plugins/platforms/wasm/qwasmaccessibility.h +@@ -56,6 +56,7 @@ private: + void handleStaticTextUpdate(QAccessibleEvent *event); + void handleButtonUpdate(QAccessibleEvent *event); + void handleCheckBoxUpdate(QAccessibleEvent *event); ++ void handleSwitchUpdate(QAccessibleEvent *event); + void handleDialogUpdate(QAccessibleEvent *event); + void handleMenuUpdate(QAccessibleEvent *event); + void handleToolUpdate(QAccessibleEvent *event); +diff --git x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +index 3417a3892115ddf303e612ab5bca62aa1d32dada..29a4a0b9651da06540a3e195b54f92ee1e7f6ab8 100644 +--- x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp ++++ y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp +@@ -83,8 +83,9 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve + { + if (QAccessibleInterface *accessible = event->accessibleInterface()) { + if (event->changedStates().checked || event->changedStates().checkStateMixed) { +- // Notifies states changes in checkboxes. +- if (accessible->role() == QAccessible::CheckBox) { ++ // Notifies states changes in checkboxes and switches. ++ if (accessible->role() == QAccessible::CheckBox ++ || accessible->role() == QAccessible::Switch) { + if (auto provider = providerForAccessible(accessible)) { + long toggleState = ToggleState_Off; + if (accessible->state().checked) +diff --git x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp +index 7536ece4dbc940a9dd171a1f46497e46c9388f9b..268961486e75ea82d26172309051056d7c61369c 100644 +--- x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp ++++ y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp +@@ -130,6 +130,7 @@ long roleToControlTypeId(QAccessible::Role role) + {QAccessible::EditableText, UIA_EditControlTypeId}, + {QAccessible::Button, UIA_ButtonControlTypeId}, + {QAccessible::CheckBox, UIA_CheckBoxControlTypeId}, ++ {QAccessible::Switch, UIA_ButtonControlTypeId}, + {QAccessible::RadioButton, UIA_RadioButtonControlTypeId}, + {QAccessible::ComboBox, UIA_ComboBoxControlTypeId}, + {QAccessible::ProgressBar, UIA_ProgressBarControlTypeId}, diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0017-Add-QAccessibleScrollInterface-to-inform-Android-Tal.patch ausweisapp2-2.4.0/libs/patches/qtbase-0017-Add-QAccessibleScrollInterface-to-inform-Android-Tal.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0017-Add-QAccessibleScrollInterface-to-inform-Android-Tal.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0017-Add-QAccessibleScrollInterface-to-inform-Android-Tal.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,268 @@ +From 951425f13bc4207b64d137fe75c74276417f2b98 Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Thu, 4 Sep 2025 08:48:59 +0200 +Subject: Add QAccessibleScrollInterface to inform Android TalkBack about + scroll + +This change introduces the QAccessibleScrollInterface to supply the +required information for Android a11y layer which will set the +relevant properties on AccessibilityEvent. Setting these will allow +TalkBack to emit the "ding" feedback about the scroll position. +For now, these sounds are only emitted for the ScrollingEnd event. +Native Android components play the "ding" during the scrolling, too. + +Task-number: QTBUG-139833 +Change-Id: Id16e74627663543cdd2918afcd8a24adce6470ff +--- + .../qt/android/QtAccessibilityDelegate.java | 3 + + .../qt/android/QtNativeAccessibility.java | 2 + + src/gui/accessible/qaccessible.cpp | 5 + + src/gui/accessible/qaccessible.h | 19 ++++ + src/gui/accessible/qaccessible_base.h | 1 + + .../android/androidjniaccessibility.cpp | 94 ++++++++++++++++++- + 6 files changed, 123 insertions(+), 1 deletion(-) + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +index 7f64928b6cd14517ecf60b63ec2a29eaec2fce4b..63a1629715ad7b14598c1eb97173419e1eead73b 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +@@ -371,6 +371,9 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + String description = QtNativeAccessibility.descriptionForAccessibleObject(virtualViewId); + event.setContentDescription(addLocaleSpan(description)); + ++ if (eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED) ++ QtNativeAccessibility.populateScrollEvent(virtualViewId, event); ++ + if (event.getText().isEmpty() && TextUtils.isEmpty(event.getContentDescription())) + Log.w(TAG, "AccessibilityEvent with empty description"); + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java +index cfef859ab05d90a38de53c0a53a8dc2d4a1e4967..36898fc3a840b16cca35918f7e76dbe3455510d0 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java +@@ -4,6 +4,7 @@ + package org.qtproject.qt.android; + + import android.graphics.Rect; ++import android.view.accessibility.AccessibilityEvent; + import android.view.accessibility.AccessibilityNodeInfo; + + class QtNativeAccessibility +@@ -21,4 +22,5 @@ class QtNativeAccessibility + static native boolean scrollBackward(int objectId); + + static native boolean populateNode(int objectId, AccessibilityNodeInfo node); ++ static native boolean populateScrollEvent(int objectId, AccessibilityEvent event); + } +diff --git x/qtbase/src/gui/accessible/qaccessible.cpp y/qtbase/src/gui/accessible/qaccessible.cpp +index a2fcb8a1be73f313d11db0b9de08167f8296e691..919e2cc21fa569ba2d28e6aed2f62966952e8928 100644 +--- x/qtbase/src/gui/accessible/qaccessible.cpp ++++ y/qtbase/src/gui/accessible/qaccessible.cpp +@@ -2572,6 +2572,11 @@ QAccessibleValueInterface::~QAccessibleValueInterface() + \l{IAccessible2 Specification} + */ + ++QAccessibleScrollInterface::~QAccessibleScrollInterface() ++{ ++} ++ ++ + /*! + Destroys the QAccessibleImageInterface. + */ +diff --git x/qtbase/src/gui/accessible/qaccessible.h y/qtbase/src/gui/accessible/qaccessible.h +index b552914ee6f75f33523defc67e9429164dff5b41..5d371c88a116fc2cf5859b0a487ffa16f3c17b19 100644 +--- x/qtbase/src/gui/accessible/qaccessible.h ++++ y/qtbase/src/gui/accessible/qaccessible.h +@@ -37,6 +37,7 @@ class QAccessible2Interface; + class QAccessibleTextInterface; + class QAccessibleEditableTextInterface; + class QAccessibleValueInterface; ++class QAccessibleScrollInterface; + class QAccessibleActionInterface; + class QAccessibleImageInterface; + class QAccessibleTableInterface; +@@ -89,6 +90,9 @@ public: + inline QAccessibleValueInterface *valueInterface() + { return reinterpret_cast(interface_cast(QAccessible::ValueInterface)); } + ++ inline QAccessibleScrollInterface *scrollInterface() ++ { return reinterpret_cast(interface_cast(QAccessible::ScrollInterface)); } ++ + inline QAccessibleActionInterface *actionInterface() + { return reinterpret_cast(interface_cast(QAccessible::ActionInterface)); } + +@@ -174,6 +178,21 @@ public: + virtual QVariant minimumStepSize() const = 0; + }; + ++class Q_GUI_EXPORT QAccessibleScrollInterface ++{ ++public: ++ virtual ~QAccessibleScrollInterface(); ++ ++ virtual qreal xPosition() const = 0; ++ virtual qreal yPosition() const = 0; ++ virtual qreal maximumXPosition() const = 0; ++ virtual qreal maximumYPosition() const = 0; ++ virtual int itemCount() const = 0; ++ virtual int fromIndex() const = 0; ++ virtual int toIndex() const = 0; ++}; ++ ++ + class Q_GUI_EXPORT QAccessibleTableCellInterface + { + public: +diff --git x/qtbase/src/gui/accessible/qaccessible_base.h y/qtbase/src/gui/accessible/qaccessible_base.h +index c4697b468de9f74202dbed24d896c173f3bad165..96445de2b88a83a498ac5996358e7804744b2980 100644 +--- x/qtbase/src/gui/accessible/qaccessible_base.h ++++ y/qtbase/src/gui/accessible/qaccessible_base.h +@@ -357,6 +357,7 @@ public: + TextInterface, + EditableTextInterface, + ValueInterface, ++ ScrollInterface, + ActionInterface, + ImageInterface, + TableInterface, +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index 4222235d05a613093dded9c63ee96a2628a82494..aaf0bce3080816710df9322561f31f028d42d34f 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -43,6 +43,13 @@ namespace QtAndroidAccessibility + static jmethodID m_setScrollableMethodID = 0; + static jmethodID m_setTextSelectionMethodID = 0; + static jmethodID m_setVisibleToUserMethodID = 0; ++ static jmethodID m_setMaxScrollXMethodID = 0; ++ static jmethodID m_setScrollXMethodID = 0; ++ static jmethodID m_setMaxScrollYMethodID = 0; ++ static jmethodID m_setScrollYMethodID = 0; ++ static jmethodID m_setFromIndexMethodID = 0; ++ static jmethodID m_setToIndexMethodID = 0; ++ static jmethodID m_setItemCountMethodID = 0; + + static bool m_accessibilityActivated = false; + +@@ -703,6 +710,7 @@ namespace QtAndroidAccessibility + info.actions.contains(QAccessibleActionInterface::increaseAction()); + const bool hasDecreaseAction = + info.actions.contains(QAccessibleActionInterface::decreaseAction()); ++ const bool scrollableRole = info.role == QAccessible::ScrollBar || info.role == QAccessible::List; + + if (info.hasTextSelection && m_setTextSelectionMethodID) { + env->CallVoidMethod(node, m_setTextSelectionMethodID, info.selectionStart, +@@ -718,7 +726,7 @@ namespace QtAndroidAccessibility + if (m_setHeadingMethodID) + env->CallVoidMethod(node, m_setHeadingMethodID, info.role == QAccessible::Heading); + env->CallVoidMethod(node, m_setVisibleToUserMethodID, !info.state.invisible); +- env->CallVoidMethod(node, m_setScrollableMethodID, hasIncreaseAction || hasDecreaseAction); ++ env->CallVoidMethod(node, m_setScrollableMethodID, hasIncreaseAction || hasDecreaseAction || scrollableRole); + env->CallVoidMethod(node, m_setClickableMethodID, hasClickableAction || info.role == QAccessible::Link); + + // Add ACTION_CLICK +@@ -744,6 +752,80 @@ namespace QtAndroidAccessibility + return true; + } + ++ struct ScrollEventInfo ++ { ++ bool validScroll = false; ++ double scrollX = 0; ++ double scrollY = 0; ++ double maxScrollY = 0; ++ double maxScrollX = 0; ++ bool validIndexScroll = false; ++ int itemCount = -1; ++ int toIndex = -1; ++ int fromIndex = -1; ++ }; ++ ++ static ScrollEventInfo populateScrollEvent_helper(int objectId) ++ { ++ QAccessibleInterface *iface = interfaceFromId(objectId); ++ if (!iface || !iface->isValid()) ++ return {}; ++ ++ QAccessibleScrollInterface *scrollIface = iface->scrollInterface(); ++ if (!scrollIface) ++ return {}; ++ ++ const int fromIndex = scrollIface->fromIndex(); ++ const int toIndex = scrollIface->toIndex(); ++ const int itemCount = scrollIface->itemCount(); ++ if (fromIndex > -1 && toIndex > -1 && itemCount > -1) { ++ ScrollEventInfo info; ++ info.validIndexScroll = true; ++ info.fromIndex = fromIndex; ++ info.toIndex = toIndex; ++ info.itemCount = itemCount; ++ return info; ++ } ++ const double maxScrollX = scrollIface->maximumXPosition(); ++ const double maxScrollY = scrollIface->maximumYPosition(); ++ if (maxScrollX > 0 || maxScrollY > 0) { ++ ScrollEventInfo info; ++ info.validScroll = true; ++ info.scrollX = scrollIface->xPosition(); ++ info.maxScrollX = maxScrollX; ++ info.scrollY = scrollIface->yPosition(); ++ info.maxScrollY = maxScrollY; ++ return info; ++ } ++ ++ return {}; ++ } ++ ++ static jboolean populateScrollEvent(JNIEnv *env, jobject /*thiz*/, jint objectId, jobject event) ++ { ++ ScrollEventInfo info; ++ if (m_accessibilityContext) { ++ runInObjectContext( ++ m_accessibilityContext, [objectId]() { return populateScrollEvent_helper(objectId); }, ++ &info); ++ } ++ ++ if (info.validIndexScroll) { ++ env->CallVoidMethod(event, m_setFromIndexMethodID, info.fromIndex); ++ env->CallVoidMethod(event, m_setToIndexMethodID, info.toIndex); ++ env->CallVoidMethod(event, m_setItemCountMethodID, info.itemCount); ++ return true; ++ } else if (info.validScroll) { ++ env->CallVoidMethod(event, m_setScrollXMethodID, (int)info.scrollX); ++ env->CallVoidMethod(event, m_setMaxScrollXMethodID, (int)info.maxScrollX); ++ env->CallVoidMethod(event, m_setScrollYMethodID, (int)info.scrollY); ++ env->CallVoidMethod(event, m_setMaxScrollYMethodID, (int)info.maxScrollY); ++ return true; ++ } ++ ++ return false; ++ } ++ + static const JNINativeMethod methods[] = { + {"setActive","(Z)V",(void*)setActive}, + {"childIdListForAccessibleObject", "(I)[I", (jintArray)childIdListForAccessibleObject}, +@@ -753,6 +835,7 @@ namespace QtAndroidAccessibility + {"screenRect", "(I)Landroid/graphics/Rect;", (jobject)screenRect}, + {"hitTest", "(FF)I", (void*)hitTest}, + {"populateNode", "(ILandroid/view/accessibility/AccessibilityNodeInfo;)Z", (void*)populateNode}, ++ {"populateScrollEvent", "(ILandroid/view/accessibility/AccessibilityEvent;)Z", (void*)populateScrollEvent}, + {"clickAction", "(I)Z", (void*)clickAction}, + {"focusAction", "(I)Z", (void*)focusAction}, + {"scrollForward", "(I)Z", (void*)scrollForward}, +@@ -792,6 +875,15 @@ namespace QtAndroidAccessibility + GET_AND_CHECK_STATIC_METHOD(m_setVisibleToUserMethodID, nodeInfoClass, "setVisibleToUser", "(Z)V"); + GET_AND_CHECK_STATIC_METHOD(m_setTextSelectionMethodID, nodeInfoClass, "setTextSelection", "(II)V"); + ++ jclass eventClass = env->FindClass("android/view/accessibility/AccessibilityEvent"); ++ GET_AND_CHECK_STATIC_METHOD(m_setMaxScrollXMethodID, eventClass, "setMaxScrollX", "(I)V"); ++ GET_AND_CHECK_STATIC_METHOD(m_setScrollXMethodID, eventClass, "setScrollX", "(I)V"); ++ GET_AND_CHECK_STATIC_METHOD(m_setMaxScrollYMethodID, eventClass, "setMaxScrollY", "(I)V"); ++ GET_AND_CHECK_STATIC_METHOD(m_setScrollYMethodID, eventClass, "setScrollY", "(I)V"); ++ GET_AND_CHECK_STATIC_METHOD(m_setFromIndexMethodID, eventClass, "setFromIndex", "(I)V"); ++ GET_AND_CHECK_STATIC_METHOD(m_setToIndexMethodID, eventClass, "setToIndex", "(I)V"); ++ GET_AND_CHECK_STATIC_METHOD(m_setItemCountMethodID, eventClass, "setItemCount", "(I)V"); ++ + return true; + } + } diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0018-Introduce-signal-ScrollingPositionChanged-to-signal-.patch ausweisapp2-2.4.0/libs/patches/qtbase-0018-Introduce-signal-ScrollingPositionChanged-to-signal-.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0018-Introduce-signal-ScrollingPositionChanged-to-signal-.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0018-Introduce-signal-ScrollingPositionChanged-to-signal-.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,68 @@ +From 912f57041b2c7f8d979f11bb4b64aac33a81ed4d Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Tue, 9 Sep 2025 08:13:29 +0200 +Subject: Introduce signal ScrollingPositionChanged to signal ongoing scrolling + +Task-number: QTBUG-139833 +Change-Id: I3b16f7c14cf49dc7b06f2d6b225d246829c0b503 +--- + src/gui/accessible/qaccessible_base.h | 31 ++++++++++--------- + .../android/qandroidplatformaccessibility.cpp | 3 +- + 2 files changed, 18 insertions(+), 16 deletions(-) + +diff --git x/qtbase/src/gui/accessible/qaccessible_base.h y/qtbase/src/gui/accessible/qaccessible_base.h +index 96445de2b88a83a498ac5996358e7804744b2980..09276aaaa16aae0286a15e3d5df97b5ad9dbe8b7 100644 +--- x/qtbase/src/gui/accessible/qaccessible_base.h ++++ y/qtbase/src/gui/accessible/qaccessible_base.h +@@ -28,21 +28,22 @@ class Q_GUI_EXPORT QAccessible + public: + + enum Event { +- SoundPlayed = 0x0001, +- Alert = 0x0002, +- ForegroundChanged = 0x0003, +- MenuStart = 0x0004, +- MenuEnd = 0x0005, +- PopupMenuStart = 0x0006, +- PopupMenuEnd = 0x0007, +- ContextHelpStart = 0x000C, +- ContextHelpEnd = 0x000D, +- DragDropStart = 0x000E, +- DragDropEnd = 0x000F, +- DialogStart = 0x0010, +- DialogEnd = 0x0011, +- ScrollingStart = 0x0012, +- ScrollingEnd = 0x0013, ++ SoundPlayed = 0x0001, ++ Alert = 0x0002, ++ ForegroundChanged = 0x0003, ++ MenuStart = 0x0004, ++ MenuEnd = 0x0005, ++ PopupMenuStart = 0x0006, ++ PopupMenuEnd = 0x0007, ++ ContextHelpStart = 0x000C, ++ ContextHelpEnd = 0x000D, ++ DragDropStart = 0x000E, ++ DragDropEnd = 0x000F, ++ DialogStart = 0x0010, ++ DialogEnd = 0x0011, ++ ScrollingStart = 0x0012, ++ ScrollingEnd = 0x0013, ++ ScrollingPositionChanged = 0x0014, + + MenuCommand = 0x0018, + +diff --git x/qtbase/src/plugins/platforms/android/qandroidplatformaccessibility.cpp y/qtbase/src/plugins/platforms/android/qandroidplatformaccessibility.cpp +index ac8b40b9a20d089473a26f347190aac0341b23c3..673b031158dc79257ae5acfd0c3f9a299453efed 100644 +--- x/qtbase/src/plugins/platforms/android/qandroidplatformaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/qandroidplatformaccessibility.cpp +@@ -34,7 +34,8 @@ void QAndroidPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent * + QtAndroidAccessibility::notifyObjectFocus(event->uniqueId()); + } else if (event->type() == QAccessible::ValueChanged) { + QtAndroidAccessibility::notifyValueChanged(event->uniqueId()); +- } else if (event->type() == QAccessible::ScrollingEnd) { ++ } else if (event->type() == QAccessible::ScrollingEnd ++ || event->type() == QAccessible::ScrollingPositionChanged) { + QtAndroidAccessibility::notifyScrolledEvent(event->uniqueId()); + } else if (event->type() == QAccessible::NameChanged + || event->type() == QAccessible::DescriptionChanged) { diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0019-a11y-macOS-Implement-accessibilityMinValue-and-acces.patch ausweisapp2-2.4.0/libs/patches/qtbase-0019-a11y-macOS-Implement-accessibilityMinValue-and-acces.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0019-a11y-macOS-Implement-accessibilityMinValue-and-acces.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0019-a11y-macOS-Implement-accessibilityMinValue-and-acces.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,48 @@ +From 23642fb1cef24700b34fd46bdc9ada9027d79955 Mon Sep 17 00:00:00 2001 +From: Julian Greilich +Date: Wed, 3 Sep 2025 15:13:00 +0200 +Subject: a11y macOS: Implement accessibilityMinValue and accessibilityMaxValue + +Use the ValueInterface to provide accessibilityMinValue and +accessibilityMaxValue. + +Task-number: QTBUG-139712 +Pick-to: 6.8 6.9 6.10 +Change-Id: I774997fe90d457ac67604ec89187a20befee6570 +Reviewed-by: Volker Hilsheimer +(cherry picked from commit 9f2fbc962b1ad29bc054764b719fd4cee9654b0e) +--- + .../cocoa/qcocoaaccessibilityelement.mm | 20 +++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git x/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm y/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +index d415bd8176a5e10813a0f97b6e99f7ad78286c5a..60920b38b69a6cf8609c6cb6e019a50e50a94b3b 100644 +--- x/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm ++++ y/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +@@ -707,6 +707,26 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of + return nil; + } + ++ ++- (id) accessibilityMinValue { ++ if (QAccessibleInterface *iface = self.qtInterface) { ++ if (iface->valueInterface()) { ++ return iface->valueInterface()->minimumValue().toString().toNSString(); ++ } ++ } ++ return nil; ++} ++ ++ ++- (id) accessibilityMaxValue { ++ if (QAccessibleInterface *iface = self.qtInterface) { ++ if (iface->valueInterface()) { ++ return iface->valueInterface()->maximumValue().toString().toNSString(); ++ } ++ } ++ return nil; ++} ++ + - (NSInteger) accessibilityNumberOfCharacters { + if (QAccessibleInterface *iface = self.qtInterface) { + if (QAccessibleTextInterface *text = iface->textInterface()) diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0020-a11y-Android-Set-a-RangeInfo-by-using-the-ValueInter.patch ausweisapp2-2.4.0/libs/patches/qtbase-0020-a11y-Android-Set-a-RangeInfo-by-using-the-ValueInter.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0020-a11y-Android-Set-a-RangeInfo-by-using-the-ValueInter.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0020-a11y-Android-Set-a-RangeInfo-by-using-the-ValueInter.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,98 @@ +From cb23031e98b6f91b5f8a5b25a162349ffaf04e9e Mon Sep 17 00:00:00 2001 +From: Julian Greilich +Date: Tue, 2 Sep 2025 12:01:15 +0200 +Subject: a11y Android: Set a RangeInfo by using the ValueInterface + +This enables components to provide more detailed information about the +value to the a11y-interface. + +For the ProgressBar this controls the pitch of the sound which TalkBack +uses to represent the current value of the ProgressBar. + +Task-number: QTBUG-139712 +Pick-to: 6.8 6.9 6.10 +Change-Id: I0b2694e8e47b264d75e3fcecbbe900c310aa4e19 +Reviewed-by: Assam Boudjelthia +(cherry picked from commit 7ae2b8bc4433b284c706dad5b431476e1d2e928c) +--- + .../android/androidjniaccessibility.cpp | 39 +++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index aaf0bce3080816710df9322561f31f028d42d34f..79697a9dc40123d708df3507a6b6e6fc85abb366 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -42,6 +42,7 @@ namespace QtAndroidAccessibility + static jmethodID m_setHeadingMethodID = 0; + static jmethodID m_setScrollableMethodID = 0; + static jmethodID m_setTextSelectionMethodID = 0; ++ static jmethodID m_setRangeInfoMethodID = 0; + static jmethodID m_setVisibleToUserMethodID = 0; + static jmethodID m_setMaxScrollXMethodID = 0; + static jmethodID m_setScrollXMethodID = 0; +@@ -664,6 +665,11 @@ namespace QtAndroidAccessibility + bool hasTextSelection = false; + int selectionStart = 0; + int selectionEnd = 0; ++ bool hasValue = false; ++ QVariant minValue = 0; ++ QVariant maxValue = 0; ++ QVariant currentValue = 0; ++ QVariant valueStepSize = 0; + }; + + static NodeInfo populateNode_helper(int objectId) +@@ -682,6 +688,14 @@ namespace QtAndroidAccessibility + info.hasTextSelection = true; + textIface->selection(0, &info.selectionStart, &info.selectionEnd); + } ++ QAccessibleValueInterface *valueInterface = iface->valueInterface(); ++ if (valueInterface) { ++ info.hasValue = true; ++ info.minValue = valueInterface->minimumValue(); ++ info.maxValue = valueInterface->maximumValue(); ++ info.currentValue = valueInterface->currentValue(); ++ info.valueStepSize = valueInterface->minimumStepSize(); ++ } + } + return info; + } +@@ -717,6 +731,28 @@ namespace QtAndroidAccessibility + info.selectionEnd); + } + ++ if (info.hasValue && m_setRangeInfoMethodID) { ++ int valueType = info.currentValue.typeId(); ++ jint rangeType = 3; // RANGE_TYPE_INDETERMINATE ++ switch (valueType) { ++ case QMetaType::Float: ++ case QMetaType::Double: ++ rangeType = 1; // RANGE_TYPE_FLOAT ++ break; ++ case QMetaType::Int: ++ rangeType = 0; // RANGE_TYPE_INT ++ break; ++ } ++ ++ QJniObject rangeInfo("android/view/accessibility/AccessibilityNodeInfo$RangeInfo", ++ "(IFFF)V", rangeType, info.minValue.toFloat(), ++ info.maxValue.toFloat(), info.currentValue.toFloat()); ++ ++ if (rangeInfo.isValid()) { ++ env->CallVoidMethod(node, m_setRangeInfoMethodID, rangeInfo.object()); ++ } ++ } ++ + env->CallVoidMethod(node, m_setCheckableMethodID, (bool)info.state.checkable); + env->CallVoidMethod(node, m_setCheckedMethodID, (bool)info.state.checked); + env->CallVoidMethod(node, m_setEditableMethodID, info.state.editable); +@@ -874,6 +910,9 @@ namespace QtAndroidAccessibility + GET_AND_CHECK_STATIC_METHOD(m_setScrollableMethodID, nodeInfoClass, "setScrollable", "(Z)V"); + GET_AND_CHECK_STATIC_METHOD(m_setVisibleToUserMethodID, nodeInfoClass, "setVisibleToUser", "(Z)V"); + GET_AND_CHECK_STATIC_METHOD(m_setTextSelectionMethodID, nodeInfoClass, "setTextSelection", "(II)V"); ++ GET_AND_CHECK_STATIC_METHOD( ++ m_setRangeInfoMethodID, nodeInfoClass, "setRangeInfo", ++ "(Landroid/view/accessibility/AccessibilityNodeInfo$RangeInfo;)V"); + + jclass eventClass = env->FindClass("android/view/accessibility/AccessibilityEvent"); + GET_AND_CHECK_STATIC_METHOD(m_setMaxScrollXMethodID, eventClass, "setMaxScrollX", "(I)V"); diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0021-a11y-Android-Use-correct-event-type-for-value-update.patch ausweisapp2-2.4.0/libs/patches/qtbase-0021-a11y-Android-Use-correct-event-type-for-value-update.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0021-a11y-Android-Use-correct-event-type-for-value-update.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0021-a11y-Android-Use-correct-event-type-for-value-update.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,40 @@ +From 834ffb6b06be6c3f4669f331f888471fff3e820d Mon Sep 17 00:00:00 2001 +From: Julian Greilich +Date: Tue, 2 Sep 2025 11:53:28 +0200 +Subject: a11y Android: Use correct event type for value update of the + ProgressBar + +This way, TalkBack plays a sound to represent the value of the +progressbar instead of announcing the value as text. + +Task-number: QTBUG-139712 +Pick-to: 6.8 6.10 +Change-Id: Ic9cd4f4cf212ba3cd3d392dacce1955d7cb2bf54 +--- + .../qtproject/qt/android/QtAccessibilityDelegate.java | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +index 63a1629715ad7b14598c1eb97173419e1eead73b..df10ce7067832da83aedce7717511be4479daa81 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +@@ -255,12 +255,15 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + return; + } + +- final AccessibilityEvent event = +- AccessibilityEvent.obtain(AccessibilityEvent.TYPE_ANNOUNCEMENT); ++ final CharSequence className = getNodeForVirtualViewId(viewId).getClassName(); ++ final int eventType = ++ className != null && className.equals("android.widget.ProgressBar") ++ ? AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED ++ : AccessibilityEvent.TYPE_ANNOUNCEMENT; ++ final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); + + event.setEnabled(true); +- event.setClassName(getNodeForVirtualViewId(viewId).getClassName()); +- ++ event.setClassName(className); + event.setContentDescription(addLocaleSpan(value)); + + if (event.getText().isEmpty() && TextUtils.isEmpty(event.getContentDescription())) { diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0022-a11y-iOS-Do-not-set-UIAccessibilityTraitAdjustable-f.patch ausweisapp2-2.4.0/libs/patches/qtbase-0022-a11y-iOS-Do-not-set-UIAccessibilityTraitAdjustable-f.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0022-a11y-iOS-Do-not-set-UIAccessibilityTraitAdjustable-f.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0022-a11y-iOS-Do-not-set-UIAccessibilityTraitAdjustable-f.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,37 @@ +From 11e017336379486fb0d3dbac19c8ba050f2f59d8 Mon Sep 17 00:00:00 2001 +From: Julian Greilich +Date: Thu, 11 Sep 2025 15:11:18 +0200 +Subject: a11y iOS: Do not set UIAccessibilityTraitAdjustable for ProgressBar +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The ProgressBar does implement the ValueInterface to represent the +current progress, but the progress is readonly. + +With the UIAccessibilityTraitAdjustable set, iOS would announce the +value of the ProgressBar as "adjustable" and give instruction to +swipe to change the value. + +Task-number: QTBUG-139712 +Pick-to: 6.10 6.9 6.8 +Change-Id: Ife4ef14b2db58217c94c3cc4edb8cd69496245fa +Reviewed-by: Tor Arne Vestbø +(cherry picked from commit d6cc7562689d8a07bc933178ea367706ca38e4ac) +--- + src/plugins/platforms/ios/quiaccessibilityelement.mm | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +index d388f2ab33bbb83ee2502acb2a55f277d081a568..586128912fd74cba60b4f8c26e739b4e42e4bb5c 100644 +--- x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm ++++ y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +@@ -193,7 +193,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + traits |= UIAccessibilityTraitStaticText; + } + +- if (iface->valueInterface()) ++ if (iface->valueInterface() && iface->role() != QAccessible::ProgressBar) + traits |= UIAccessibilityTraitAdjustable; + + return traits; diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0023-Android-Fix-double-emission-of-certain-a11y-events.patch ausweisapp2-2.4.0/libs/patches/qtbase-0023-Android-Fix-double-emission-of-certain-a11y-events.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0023-Android-Fix-double-emission-of-certain-a11y-events.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0023-Android-Fix-double-emission-of-certain-a11y-events.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,83 @@ +From 063cd7e7491900384d1448795c75c93e56e558fc Mon Sep 17 00:00:00 2001 +From: Timon Sassor +Date: Mon, 15 Sep 2025 13:32:26 +0200 +Subject: Android: Fix double emission of certain a11y events +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +3c709198838866d5122c69a30cacdc806605d0cf and +e7a211d702d6aa21a30eff56ceeffc153385443e introduced a11y actions for +focusing and scrolling. However they also accidentally introduced a +double emission of their according events. This causes problems, since +every new event stops the processing and announcement of the previous in +TalkBack. + +We remove the sendEventForVirtualViewId() calls from performAction() and +performActionForVirtualViewId(), because they are the culprit. If an +action like focus or scroll changes the app state, an a11y update is +triggered, which ends up calling notifyObjectFocus() or +notifyScrolledEvent() where the according event is then emitted and the +a11y delegate state is updated. + +For the focus there is an exception, since an element cannot gain the +active focus if is is disabled. However TalkBack should still be able to +place its focus frame on that element and read out its content. + +Pick-to: 6.10 6.8 +Change-Id: I9061eec713fd5382feb7aacafdffe393968ef8f1 +Reviewed-by: Lars Schmertmann +Reviewed-by: Morten Johan Sørvig +Reviewed-by: Assam Boudjelthia +(cherry picked from commit 249cec4c5c6a438b8a201b14157f38a3f7a3fad2) +--- + .../qt/android/QtAccessibilityDelegate.java | 23 ++++++------------- + 1 file changed, 7 insertions(+), 16 deletions(-) + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +index df10ce7067832da83aedce7717511be4479daa81..db63acd4153376fe965d655a6aecb60b90daa8cf 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +@@ -542,17 +542,6 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + boolean handled = false; + //Log.i(TAG, "PERFORM ACTION: " + action + " on " + virtualViewId); + switch (action) { +- case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: +- // Only handle the FOCUS action if it's placing focus on +- // a different view that was previously focused. +- if (m_focusedVirtualViewId != virtualViewId) { +- m_focusedVirtualViewId = virtualViewId; +- m_view.invalidate(); +- sendEventForVirtualViewId(virtualViewId, +- AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); +- handled = true; +- } +- break; + case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: + if (m_focusedVirtualViewId == virtualViewId) { + m_focusedVirtualViewId = INVALID_ID; +@@ -593,17 +582,19 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_VIEW_CLICKED); + break; + case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: +- success = QtNativeAccessibility.focusAction(virtualViewId); ++ if (m_focusedVirtualViewId != virtualViewId) { ++ success = QtNativeAccessibility.focusAction(virtualViewId); ++ if (!success) { ++ notifyObjectFocus(virtualViewId); ++ success = true; ++ } ++ } + break; + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: + success = QtNativeAccessibility.scrollForward(virtualViewId); +- if (success) +- sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_VIEW_SCROLLED); + break; + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: + success = QtNativeAccessibility.scrollBackward(virtualViewId); +- if (success) +- sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_VIEW_SCROLLED); + break; + } + return success; diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0024-Android-Avoid-announcing-simple-text-as-clickable.patch ausweisapp2-2.4.0/libs/patches/qtbase-0024-Android-Avoid-announcing-simple-text-as-clickable.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0024-Android-Avoid-announcing-simple-text-as-clickable.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0024-Android-Avoid-announcing-simple-text-as-clickable.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,41 @@ +From ed1a5d7a6d7cea37270753772a51d35c7e48d4da Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Mon, 15 Sep 2025 16:06:43 +0200 +Subject: Android: Avoid announcing simple text as clickable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The definition of Accessible.onPressAction is enough to let +Android announce an element as clickable. But this is a +problem when components are designied in a way like this: + Accessible.role: clickable ? Accessible.Button : Accessible.StaticText + Accessible.onPressAction: clickable ? doit() : nothing() +So we avoid to set clickable for StaticText and Heading. +Windows, macOS and iOS already have the right behavior and do +not care about pressAction or toggleAction on simple text. + +Pick-to: 6.10 6.9 6.8 +Change-Id: I7bf9ffae112b92dd4a90f51f014a348dfe1b63f3 +Reviewed-by: Tor Arne Vestbø +(cherry picked from commit ac7b5815db7f08b24fbd598e22c13652cae3bb2f) +--- + src/plugins/platforms/android/androidjniaccessibility.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index 79697a9dc40123d708df3507a6b6e6fc85abb366..a6e587291d79b75cc17bfed4a8f5803b6c132022 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -718,8 +718,9 @@ namespace QtAndroidAccessibility + env->CallVoidMethod(node, m_setClassNameMethodID, jrole); + + const bool hasClickableAction = +- info.actions.contains(QAccessibleActionInterface::pressAction()) || +- info.actions.contains(QAccessibleActionInterface::toggleAction()); ++ (info.actions.contains(QAccessibleActionInterface::pressAction()) ++ || info.actions.contains(QAccessibleActionInterface::toggleAction())) ++ && !(info.role == QAccessible::StaticText || info.role == QAccessible::Heading); + const bool hasIncreaseAction = + info.actions.contains(QAccessibleActionInterface::increaseAction()); + const bool hasDecreaseAction = diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0025-Revert-Android-Keyboard-Use-new-show-hide-methods-fo.patch ausweisapp2-2.4.0/libs/patches/qtbase-0025-Revert-Android-Keyboard-Use-new-show-hide-methods-fo.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0025-Revert-Android-Keyboard-Use-new-show-hide-methods-fo.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0025-Revert-Android-Keyboard-Use-new-show-hide-methods-fo.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,172 @@ +From 7d9ff000f34b1906900ca6e82d46fe88c0221e0b Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Mon, 22 Sep 2025 11:44:08 +0200 +Subject: Revert "Android-Keyboard: Use new show() / hide() methods for + keyboard control" + +This reverts commit 2aae64c041e81f7c982b3d3a72fc78ea7ce00a5a. + +Change-Id: I68729f6b91bf02083d49e883439ea18f9e97c823 +--- + .../qtproject/qt/android/QtInputDelegate.java | 119 ++++++------------ + 1 file changed, 40 insertions(+), 79 deletions(-) + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java +index b0f0064230710b5445912dbaa96ea7d1a05d1a6d..9536ca920de096eaa40558e00ade52a957af4eaa 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtInputDelegate.java +@@ -19,8 +19,6 @@ import android.view.KeyEvent; + import android.view.MotionEvent; + import android.view.WindowInsets; + import android.view.WindowInsets.Type; +-import android.view.WindowInsetsAnimationController; +-import android.view.WindowInsetsAnimationControlListener; + import android.view.WindowManager; + import android.view.View; + import android.view.ViewTreeObserver; +@@ -142,57 +140,6 @@ class QtInputDelegate implements QtInputConnection.QtInputConnectionListener, Qt + } + } + +- private void showKeyboard(Activity activity, +- final int x, final int y, final int width, final int height, +- final int inputHints, final int enterKeyType) +- { +- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { +- activity.getWindow().getInsetsController().controlWindowInsetsAnimation( +- WindowInsets.Type.ime(), -1, null, null, +- new WindowInsetsAnimationControlListener() { +- @Override +- public void onCancelled(WindowInsetsAnimationController controller) { } +- +- @Override +- public void onReady(WindowInsetsAnimationController controller, int types) { } +- +- @Override +- public void onFinished(WindowInsetsAnimationController controller) { +- QtNativeInputConnection.updateCursorPosition(); +- if (m_softInputMode == 0) +- probeForKeyboardHeight(activity, x, y, width, height, +- inputHints, enterKeyType); +- } +- }); +- activity.getWindow().getInsetsController().show(Type.ime()); +- } else { +- if (m_imm == null) +- return; +- m_imm.showSoftInput(m_currentEditText, 0, new ResultReceiver(new Handler()) { +- @Override +- @SuppressWarnings("fallthrough") +- protected void onReceiveResult(int resultCode, Bundle resultData) { +- switch (resultCode) { +- case InputMethodManager.RESULT_SHOWN: +- QtNativeInputConnection.updateCursorPosition(); +- //FALLTHROUGH +- case InputMethodManager.RESULT_UNCHANGED_SHOWN: +- setKeyboardVisibility(true, System.nanoTime()); +- if (m_softInputMode == 0) { +- probeForKeyboardHeight(activity, +- x, y, width, height, inputHints, enterKeyType); +- } +- break; +- case InputMethodManager.RESULT_HIDDEN: +- case InputMethodManager.RESULT_UNCHANGED_HIDDEN: +- setKeyboardVisibility(false, System.nanoTime()); +- break; +- } +- } +- }); +- } +- } +- + @Override + public void showSoftwareKeyboard(Activity activity, + final int x, final int y, final int width, final int height, +@@ -212,7 +159,30 @@ class QtInputDelegate implements QtInputConnection.QtInputConnectionListener, Qt + m_currentEditText.setLayoutParams(new QtLayout.LayoutParams(width, height, x, y)); + m_currentEditText.requestFocus(); + m_currentEditText.postDelayed(() -> { +- showKeyboard(activity, x, y, width, height, inputHints, enterKeyType); ++ if (m_imm == null) ++ return; ++ m_imm.showSoftInput(m_currentEditText, 0, new ResultReceiver(new Handler()) { ++ @Override ++ @SuppressWarnings("fallthrough") ++ protected void onReceiveResult(int resultCode, Bundle resultData) { ++ switch (resultCode) { ++ case InputMethodManager.RESULT_SHOWN: ++ QtNativeInputConnection.updateCursorPosition(); ++ //FALLTHROUGH ++ case InputMethodManager.RESULT_UNCHANGED_SHOWN: ++ setKeyboardVisibility(true, System.nanoTime()); ++ if (m_softInputMode == 0) { ++ probeForKeyboardHeight(activity, ++ x, y, width, height, inputHints, enterKeyType); ++ } ++ break; ++ case InputMethodManager.RESULT_HIDDEN: ++ case InputMethodManager.RESULT_UNCHANGED_HIDDEN: ++ setKeyboardVisibility(false, System.nanoTime()); ++ break; ++ } ++ } ++ }); + if (m_currentEditText.m_optionsChanged) { + m_imm.restartInput(m_currentEditText); + m_currentEditText.m_optionsChanged = false; +@@ -271,31 +241,22 @@ class QtInputDelegate implements QtInputConnection.QtInputConnectionListener, Qt + if (m_imm == null || m_currentEditText == null) + return; + +- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { +- Activity activity = QtNative.activity(); +- if (activity == null) { +- Log.w(TAG, "hideSoftwareKeyboard: The activity reference is null"); +- return; +- } +- activity.getWindow().getInsetsController().hide(Type.ime()); +- } else { +- m_imm.hideSoftInputFromWindow(m_currentEditText.getWindowToken(), 0, +- new ResultReceiver(new Handler()) { +- @Override +- protected void onReceiveResult(int resultCode, Bundle resultData) { +- switch (resultCode) { +- case InputMethodManager.RESULT_SHOWN: +- case InputMethodManager.RESULT_UNCHANGED_SHOWN: +- setKeyboardVisibility(true, System.nanoTime()); +- break; +- case InputMethodManager.RESULT_HIDDEN: +- case InputMethodManager.RESULT_UNCHANGED_HIDDEN: +- setKeyboardVisibility(false, System.nanoTime()); +- break; +- } ++ m_imm.hideSoftInputFromWindow(m_currentEditText.getWindowToken(), 0, ++ new ResultReceiver(new Handler()) { ++ @Override ++ protected void onReceiveResult(int resultCode, Bundle resultData) { ++ switch (resultCode) { ++ case InputMethodManager.RESULT_SHOWN: ++ case InputMethodManager.RESULT_UNCHANGED_SHOWN: ++ setKeyboardVisibility(true, System.nanoTime()); ++ break; ++ case InputMethodManager.RESULT_HIDDEN: ++ case InputMethodManager.RESULT_UNCHANGED_HIDDEN: ++ setKeyboardVisibility(false, System.nanoTime()); ++ break; + } +- }); +- } ++ } ++ }); + }); + } + +@@ -321,7 +282,7 @@ class QtInputDelegate implements QtInputConnection.QtInputConnectionListener, Qt + return true; + } + +- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { ++ if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + Rect r = new Rect(); + activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(r); + DisplayMetrics metrics = new DisplayMetrics(); diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0026-a11y-Android-Enable-granular-text-navigation-for-edi.patch ausweisapp2-2.4.0/libs/patches/qtbase-0026-a11y-Android-Enable-granular-text-navigation-for-edi.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0026-a11y-Android-Enable-granular-text-navigation-for-edi.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0026-a11y-Android-Enable-granular-text-navigation-for-edi.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,281 @@ +From 58dcacd193ca34a007f308549b62f9dc7a11fc13 Mon Sep 17 00:00:00 2001 +From: Timon Sassor +Date: Fri, 5 Sep 2025 11:38:43 +0200 +Subject: a11y Android: Enable granular text navigation for editable components + +Talkback allows it to change the text navigation on editable components +between character, word, line, sentence etc.. However the Android +platform plugin does not listen to these gestures and does not pass them +to editable components like TextEdit. +So we listen to these actions and provide the appropriate events. +QAccessibleTextInterface already has the ability to navigate text as +described. So we add the missing link here, which is the support in the +accessibility plugin. + +[ChangeLog][Accessibility][Android] Enable granular text navigation for +editable components e.g. TextEdit + +Fixes: QTBUG-139943 +Change-Id: I118222bc23c192d3bc9198f639166f39801691e8 +--- + .../qt/android/QtAccessibilityDelegate.java | 34 ++++- + .../qt/android/QtNativeAccessibility.java | 3 + + .../android/androidjniaccessibility.cpp | 117 ++++++++++++++++++ + 3 files changed, 152 insertions(+), 2 deletions(-) + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +index db63acd4153376fe965d655a6aecb60b90daa8cf..81db2da44d866e12c454af04f5e5c1d16ddf6f9b 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +@@ -43,6 +43,8 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + // When exploring the screen by touch, the item "hovered" by the finger. + private int m_hoveredVirtualViewId = INVALID_ID; + ++ private int m_currentRotorSetting = AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER; ++ + // Cache coordinates of the view to know the global offset + // this is because the Android platform window does not take + // the offset of the view on screen into account (eg status bar on top) +@@ -382,6 +384,12 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + + event.setPackageName(m_view.getContext().getPackageName()); + event.setSource(m_view, virtualViewId); ++ ++ if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY) { ++ event.setMovementGranularity(m_currentRotorSetting); ++ QtNativeAccessibility.populateGranularMovementEvent(virtualViewId, event); ++ } ++ + return event; + } + +@@ -497,6 +505,17 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + screenRect.offset(-parentScreenRect.left, -parentScreenRect.top); + node.setBoundsInParent(screenRect); + ++ if (node.isEnabled() && node.isEditable()) { ++ node.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY); ++ node.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY); ++ node.setMovementGranularities( ++ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER | ++ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD | ++ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE | ++ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH ++ ); ++ } ++ + // Manage internal accessibility focus state. + if (m_focusedVirtualViewId == virtualViewId) { + node.setAccessibilityFocused(true); +@@ -561,13 +580,13 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + return m_view.performAccessibilityAction(action, arguments); + } + } +- handled |= performActionForVirtualViewId(virtualViewId, action); ++ handled |= performActionForVirtualViewId(virtualViewId, action, arguments); + + return handled; + } + }; + +- protected boolean performActionForVirtualViewId(int virtualViewId, int action) ++ protected boolean performActionForVirtualViewId(int virtualViewId, int action, Bundle arguments) + { + //noinspection CommentedOutCode + { +@@ -596,6 +615,17 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: + success = QtNativeAccessibility.scrollBackward(virtualViewId); + break; ++ case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY: ++ case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: ++ m_currentRotorSetting = arguments.getInt( ++ AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, ++ AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER // Default-Value, if not set ++ ); ++ final boolean movingForward = action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY; ++ success = QtNativeAccessibility.moveToNextElementAtGranularity(virtualViewId, movingForward, m_currentRotorSetting); ++ if (success) ++ sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); ++ break; + } + return success; + } +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java +index 36898fc3a840b16cca35918f7e76dbe3455510d0..9074e589a779c0bf20c383f0cb0fd40642d49111 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java +@@ -6,6 +6,7 @@ package org.qtproject.qt.android; + import android.graphics.Rect; + import android.view.accessibility.AccessibilityEvent; + import android.view.accessibility.AccessibilityNodeInfo; ++import android.view.accessibility.AccessibilityEvent; + + class QtNativeAccessibility + { +@@ -20,7 +21,9 @@ class QtNativeAccessibility + static native boolean focusAction(int objectId); + static native boolean scrollForward(int objectId); + static native boolean scrollBackward(int objectId); ++ static native boolean moveToNextElementAtGranularity(int objectId, boolean forward, int granularity); + + static native boolean populateNode(int objectId, AccessibilityNodeInfo node); + static native boolean populateScrollEvent(int objectId, AccessibilityEvent event); ++ static native boolean populateGranularMovementEvent(int objectId, AccessibilityEvent event); + } +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index a6e587291d79b75cc17bfed4a8f5803b6c132022..d1611aef02fc24f7b6fd38a4e29b2a2fa0079aaa 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -52,6 +52,17 @@ namespace QtAndroidAccessibility + static jmethodID m_setToIndexMethodID = 0; + static jmethodID m_setItemCountMethodID = 0; + ++ static jmethodID m_getMovementGranularityEventMethodID = 0; ++ static jmethodID m_setFromIndexEventMethodID = 0; ++ static jmethodID m_setToIndexEventMethodID = 0; ++ ++ static const QMap m_movementGranularityMapping { ++ std::make_pair(QAccessible::TextBoundaryType::CharBoundary, QJniObject::getStaticField("android/view/accessibility/AccessibilityNodeInfo", "MOVEMENT_GRANULARITY_CHARACTER")), ++ std::make_pair(QAccessible::TextBoundaryType::WordBoundary, QJniObject::getStaticField("android/view/accessibility/AccessibilityNodeInfo", "MOVEMENT_GRANULARITY_WORD")), ++ std::make_pair(QAccessible::TextBoundaryType::LineBoundary, QJniObject::getStaticField("android/view/accessibility/AccessibilityNodeInfo", "MOVEMENT_GRANULARITY_LINE")), ++ std::make_pair(QAccessible::TextBoundaryType::ParagraphBoundary, QJniObject::getStaticField("android/view/accessibility/AccessibilityNodeInfo", "MOVEMENT_GRANULARITY_PARAGRAPH")) ++ }; ++ + static bool m_accessibilityActivated = false; + + // This object is needed to schedule the execution of the code that +@@ -413,6 +424,50 @@ namespace QtAndroidAccessibility + return result && oldPosition != screenRect_helper(firstChildId, false); + } + ++ static bool moveToNextElementAtGranularity_helper(int objectId, bool forward, int granularity) ++ { ++ QAccessibleInterface *iface = interfaceFromId(objectId); ++ if (!iface || !iface->isValid() || !iface->textInterface()) ++ return false; ++ ++ QAccessibleTextInterface *textIface = iface->textInterface(); ++ ++ Q_ASSERT(m_movementGranularityMapping.first() != 0); ++ const auto movementGranularity = m_movementGranularityMapping.key(granularity, QAccessible::TextBoundaryType::NoBoundary); ++ const int cursorPosition = textIface->cursorPosition(); ++ const int charCount = textIface->characterCount(); ++ const bool wouldMoveOutsideBounds = (!forward && cursorPosition == 0) || (forward && cursorPosition == charCount); ++ if (movementGranularity == QAccessible::TextBoundaryType::NoBoundary || wouldMoveOutsideBounds) ++ return false; ++ ++ int fromIndex = 0; ++ int toIndex = 0; ++ if (forward) ++ textIface->textAfterOffset(cursorPosition, movementGranularity, &fromIndex, &toIndex); ++ else ++ textIface->textBeforeOffset(cursorPosition, movementGranularity, &fromIndex, &toIndex); ++ ++ if (toIndex < 0) ++ textIface->setCursorPosition(charCount); ++ else if (fromIndex < 0) ++ textIface->setCursorPosition(0); ++ else ++ textIface->setCursorPosition(fromIndex); ++ ++ return true; ++ } ++ ++ static jboolean moveToNextElementAtGranularity(JNIEnv */*env*/, jobject /*thiz*/, jint objectId, jboolean forward, jint granularity) ++ { ++ bool result = false; ++ if (m_accessibilityContext) { ++ runInObjectContext(m_accessibilityContext, [objectId, forward, granularity]() { ++ return moveToNextElementAtGranularity_helper(objectId, forward, granularity); ++ }, &result); ++ } ++ return result; ++ } ++ + static QString textFromValue(QAccessibleInterface *iface) + { + QString valueStr; +@@ -863,6 +918,61 @@ namespace QtAndroidAccessibility + return false; + } + ++ struct GranularMovementEventInfo ++ { ++ bool valid = false; ++ int fromIndex = 0; ++ int toIndex = 0; ++ }; ++ ++ static GranularMovementEventInfo populateGranularMovementEvent_helper(int objectId, int movementGranularity) ++ { ++ GranularMovementEventInfo info; ++ QAccessibleInterface *iface = interfaceFromId(objectId); ++ if (!iface || !iface->isValid()) ++ return info; ++ ++ info.valid = true; ++ ++ QAccessibleTextInterface *textIface = iface->textInterface(); ++ if (!textIface) ++ return info; ++ ++ const auto mappedMovementGranularity = m_movementGranularityMapping.key(movementGranularity, QAccessible::TextBoundaryType::NoBoundary); ++ if (mappedMovementGranularity != QAccessible::TextBoundaryType::NoBoundary) { ++ // Since this method is called after the cursor already moved, we only have to look left of the cursor. ++ textIface->textBeforeOffset( ++ textIface->cursorPosition(), ++ mappedMovementGranularity, ++ &info.fromIndex, ++ &info.toIndex ++ ); ++ } ++ ++ return info; ++ } ++ ++ static jboolean populateGranularMovementEvent(JNIEnv *env, jobject /*thiz*/, jint objectId, jobject event) ++ { ++ GranularMovementEventInfo info; ++ const jint movementGranularity = m_movementGranularityMapping.first(); ++ env->CallIntMethod(event, m_getMovementGranularityEventMethodID, &movementGranularity); ++ if (m_accessibilityContext) { ++ runInObjectContext(m_accessibilityContext, [objectId, &movementGranularity]() { ++ return populateGranularMovementEvent_helper(objectId, movementGranularity); ++ }, &info); ++ } ++ if (!info.valid) { ++ __android_log_print(ANDROID_LOG_WARN, m_qtTag, "Accessibility: populateGranularMovementEvent for Invalid ID"); ++ return false; ++ } ++ ++ env->CallVoidMethod(event, m_setFromIndexEventMethodID, info.fromIndex); ++ env->CallVoidMethod(event, m_setToIndexEventMethodID, info.toIndex); ++ ++ return true; ++ } ++ + static const JNINativeMethod methods[] = { + {"setActive","(Z)V",(void*)setActive}, + {"childIdListForAccessibleObject", "(I)[I", (jintArray)childIdListForAccessibleObject}, +@@ -877,6 +987,8 @@ namespace QtAndroidAccessibility + {"focusAction", "(I)Z", (void*)focusAction}, + {"scrollForward", "(I)Z", (void*)scrollForward}, + {"scrollBackward", "(I)Z", (void*)scrollBackward}, ++ {"moveToNextElementAtGranularity", "(IZI)Z", (void *)moveToNextElementAtGranularity}, ++ {"populateGranularMovementEvent", "(ILandroid/view/accessibility/AccessibilityEvent;)Z", (void *)populateGranularMovementEvent} + }; + + #define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ +@@ -924,6 +1036,11 @@ namespace QtAndroidAccessibility + GET_AND_CHECK_STATIC_METHOD(m_setToIndexMethodID, eventClass, "setToIndex", "(I)V"); + GET_AND_CHECK_STATIC_METHOD(m_setItemCountMethodID, eventClass, "setItemCount", "(I)V"); + ++ jclass accessibilityEventClass = env->FindClass("android/view/accessibility/AccessibilityEvent"); ++ GET_AND_CHECK_STATIC_METHOD(m_getMovementGranularityEventMethodID, accessibilityEventClass, "getMovementGranularity", "()I"); ++ GET_AND_CHECK_STATIC_METHOD(m_setFromIndexEventMethodID, accessibilityEventClass, "setFromIndex", "(I)V"); ++ GET_AND_CHECK_STATIC_METHOD(m_setToIndexEventMethodID, accessibilityEventClass, "setToIndex", "(I)V"); ++ + return true; + } + } diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0027-Revert-iOS-Pause-display-link-when-app-moves-out-of-.patch ausweisapp2-2.4.0/libs/patches/qtbase-0027-Revert-iOS-Pause-display-link-when-app-moves-out-of-.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0027-Revert-iOS-Pause-display-link-when-app-moves-out-of-.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0027-Revert-iOS-Pause-display-link-when-app-moves-out-of-.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,48 @@ +From dfa624f8532137533e7cf4f62ef8c553ed93d68d Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Wed, 24 Sep 2025 08:05:54 +0200 +Subject: Revert "iOS: Pause display link when app moves out of active state" + +This reverts commit 1b993a787d7e1e2ed7bde8102847424db676d2c7. +--- + src/plugins/platforms/ios/qiosscreen.mm | 20 -------------------- + 1 file changed, 20 deletions(-) + +diff --git x/qtbase/src/plugins/platforms/ios/qiosscreen.mm y/qtbase/src/plugins/platforms/ios/qiosscreen.mm +index 3d99b4fb92fa90dfd0bdaaa985604b1496b4d56e..472b81d30a07d4f64ecf48b63ec4e40898e65ddf 100644 +--- x/qtbase/src/plugins/platforms/ios/qiosscreen.mm ++++ y/qtbase/src/plugins/platforms/ios/qiosscreen.mm +@@ -180,15 +180,6 @@ QIOSScreen::QIOSScreen(UIScreen *screen) + m_displayLink.paused = YES; // Enabled when clients call QWindow::requestUpdate() + [m_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + +- // We're pausing the display link if the application moves out of the active state, +- // so make sure to deliver to any windows that need it once the app becomes active. +- QObject::connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, [this](auto newState) { +- if (newState == Qt::ApplicationActive) { +- qCDebug(lcQpaApplication) << "Attempting update request delivery after becoming active"; +- deliverUpdateRequests(); +- } +- }); +- + #endif // !defined(Q_OS_VISIONOS)) + + updateProperties(); +@@ -274,17 +265,6 @@ void QIOSScreen::deliverUpdateRequests() const + { + bool pauseUpdates = true; + +- if (QGuiApplication::applicationState() != Qt::ApplicationActive) { +- // The applicationWillResignActive documentation describes that the app +- // should "use this method to pause ongoing tasks, disable timers, and +- // throttle down OpenGL ES frame rates", so we skip update request +- // delivery if the app is not active. Once it becomes active again +- // we re-try the update request delivery (see QIOSScreen constructor). +- qCDebug(lcQpaApplication) << "Skipping update request delivery and pausing display link"; +- m_displayLink.paused = true; +- return; +- } +- + QList windows = QGuiApplication::allWindows(); + for (int i = 0; i < windows.size(); ++i) { + QWindow *window = windows.at(i); diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0028-a11y-iOS-Enable-granular-text-navigation-for-editabl.patch ausweisapp2-2.4.0/libs/patches/qtbase-0028-a11y-iOS-Enable-granular-text-navigation-for-editabl.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0028-a11y-iOS-Enable-granular-text-navigation-for-editabl.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0028-a11y-iOS-Enable-granular-text-navigation-for-editabl.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,259 @@ +From 5865482b3945a518a1a2a3aa12ea46136e26dcd5 Mon Sep 17 00:00:00 2001 +From: Timon Sassor +Date: Fri, 12 Sep 2025 14:20:50 +0200 +Subject: a11y iOS: Enable granular text navigation for editable components + +VoiceOver has the Rotor that allows it to change the text navigation on +editable components between character, word, line, sentence etc.. +However the ios platform plugin does not listen to these gestures and +does not pass them to editable components like TextEdit. +So we listen to these actions and provide the appropriate events. +QAccessibleTextInterface already has the ability to navigate text as +described. So we add the missing link here, which is the support in the +accessibility plugin. +Also add a local translation for the rotor names, since they must not +depend on the app language but on the OS language. + +[ChangeLog][Accessibility][iOS] Enable granular text navigation for +editable components e.g. TextEdit + +Fixes: QTBUG-139943 +Change-Id: I2ff9c06140256de4e49ee4d4e0a47f8640bc0837 +--- + src/plugins/platforms/ios/qiostextresponder.h | 18 +++ + .../platforms/ios/qiostextresponder.mm | 15 +-- + .../platforms/ios/quiaccessibilityelement.mm | 110 +++++++++++++++++- + src/plugins/platforms/ios/uistrings.cpp | 4 + + src/plugins/platforms/ios/uistrings_p.h | 4 + + 5 files changed, 134 insertions(+), 17 deletions(-) + +diff --git x/qtbase/src/plugins/platforms/ios/qiostextresponder.h y/qtbase/src/plugins/platforms/ios/qiostextresponder.h +index 491dc0b6323b3152c17bd80f090f7e1dc4fd45a5..ab4deac5a5e866c835b406a6ca11d2f00ae2a1c4 100644 +--- x/qtbase/src/plugins/platforms/ios/qiostextresponder.h ++++ y/qtbase/src/plugins/platforms/ios/qiostextresponder.h +@@ -49,3 +49,21 @@ QT_END_NAMESPACE + @property(nonatomic, assign) id inputDelegate; + + @end ++ ++// ------------------------------------------------------------------------- ++ ++@interface QUITextPosition : UITextPosition ++ ++@property (nonatomic) NSUInteger index; +++ (instancetype)positionWithIndex:(NSUInteger)index; ++ ++@end ++ ++// ------------------------------------------------------------------------- ++ ++@interface QUITextRange : UITextRange ++ ++@property (nonatomic) NSRange range; +++ (instancetype)rangeWithNSRange:(NSRange)range; ++ ++@end +diff --git x/qtbase/src/plugins/platforms/ios/qiostextresponder.mm y/qtbase/src/plugins/platforms/ios/qiostextresponder.mm +index df07d8d6bf8c7b92a4897a8dac9869e830e9ce8e..d4f5e3050ae5788b1c5d46ade709533e86d7a5ce 100644 +--- x/qtbase/src/plugins/platforms/ios/qiostextresponder.mm ++++ y/qtbase/src/plugins/platforms/ios/qiostextresponder.mm +@@ -14,14 +14,8 @@ + #include + #include + +-// ------------------------------------------------------------------------- +- +-@interface QUITextPosition : UITextPosition +- +-@property (nonatomic) NSUInteger index; +-+ (instancetype)positionWithIndex:(NSUInteger)index; + +-@end ++// ------------------------------------------------------------------------- + + @implementation QUITextPosition + +@@ -36,13 +30,6 @@ + + // ------------------------------------------------------------------------- + +-@interface QUITextRange : UITextRange +- +-@property (nonatomic) NSRange range; +-+ (instancetype)rangeWithNSRange:(NSRange)range; +- +-@end +- + @implementation QUITextRange + + + (instancetype)rangeWithNSRange:(NSRange)nsrange +diff --git x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +index 586128912fd74cba60b4f8c26e739b4e42e4bb5c..98d107b8ed65c3ede133f486eb49094aaac69c68 100644 +--- x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm ++++ y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +@@ -2,6 +2,7 @@ + // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + + #include "quiaccessibilityelement.h" ++#include "qiostextresponder.h" + + #if QT_CONFIG(accessibility) + +@@ -125,9 +126,14 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + QAccessibleValueInterface *val = iface->valueInterface(); + if (val) { + return val->currentValue().toString().toNSString(); +- } else if (iface->editableTextInterface()) { +- if (QAccessibleTextInterface *text = iface->textInterface()) +- return text->text(0, text->characterCount()).toNSString(); ++ } else if (iface->role() == QAccessible::Role::EditableText) { ++ if (QAccessibleTextInterface *text = iface->textInterface()) { ++ int start = 0; ++ int end = text->characterCount(); ++ if (text->selectionCount() > 0) ++ text->selection(0, &start, &end); ++ return text->text(start, end).toNSString(); ++ } + } + + return [super accessibilityHint]; +@@ -183,6 +189,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + return textField.accessibilityTraits; + }(); + traits |= defaultTextFieldTraits; ++ traits |= UIAccessibilityTraitUpdatesFrequently; + } else if (accessibleRole == QAccessible::Graphic) { + traits |= UIAccessibilityTraitImage; + } else if (accessibleRole == QAccessible::Heading) { +@@ -277,6 +284,103 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); + return NO; + } + ++struct RotorNameTranslator { ++ RotorNameTranslator() : chars(A11Y_ROTOR_NAME_CHARS), words(A11Y_ROTOR_NAME_WORDS), sentences(A11Y_ROTOR_NAME_SENTENCES) { ++ const auto fileName = QString(":/translations/qtbase_%1.qm").arg(QLocale::system().bcp47Name()); ++ auto translator = QTranslator(); ++ if (translator.load(fileName)) { ++ chars = translator.translate(ACCESSIBILITY_ELEMENT, A11Y_ROTOR_NAME_CHARS); ++ words = translator.translate(ACCESSIBILITY_ELEMENT, A11Y_ROTOR_NAME_WORDS); ++ sentences = translator.translate(ACCESSIBILITY_ELEMENT, A11Y_ROTOR_NAME_SENTENCES); ++ } ++ } ++ QString chars; ++ QString words; ++ QString sentences; ++}; ++ ++- (NSArray *)accessibilityCustomRotors ++{ ++ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); ++ if (!iface || iface->role() != QAccessible::EditableText) ++ return nil; ++ ++ const static auto rotorNameTranslator = RotorNameTranslator(); ++ auto getRotorName = [](QAccessible::TextBoundaryType stepSize) { ++ switch (stepSize) { ++ case QAccessible::TextBoundaryType::WordBoundary: ++ return rotorNameTranslator.words; ++ case QAccessible::TextBoundaryType::SentenceBoundary: ++ return rotorNameTranslator.sentences; ++ default: ++ return rotorNameTranslator.chars; ++ } ++ }; ++ ++ auto createRotor = [&getRotorName, self](QAccessible::TextBoundaryType stepSize) { ++ UIAccessibilityCustomRotor *rotor = [[UIAccessibilityCustomRotor alloc] ++ initWithName:getRotorName(stepSize).toNSString() ++ itemSearchBlock:^UIAccessibilityCustomRotorItemResult * (UIAccessibilityCustomRotorSearchPredicate *predicate) ++ { ++ QAccessibleTextInterface *textIface = QAccessible::accessibleInterface(self.axid)->textInterface(); ++ if (!textIface) ++ return nil; ++ ++ textIface->removeSelection(0); ++ const int cursorPosition = textIface->cursorPosition(); ++ const int charCount = textIface->characterCount(); ++ const bool forward = predicate.searchDirection == UIAccessibilityCustomRotorDirectionNext; ++ ++ if ((cursorPosition == 0 && !forward) || (cursorPosition == charCount && forward)) ++ return nil; ++ ++ int fromIndex = 0; ++ int toIndex = 0; ++ ++ if (forward) { ++ textIface->textAfterOffset(cursorPosition, stepSize, &fromIndex, &toIndex); ++ if (toIndex < 0) ++ textIface->textBeforeOffset(charCount, stepSize, &fromIndex, &toIndex); ++ } else { ++ textIface->textBeforeOffset(cursorPosition, stepSize, &fromIndex, &toIndex); ++ // This adjustment is required to place the cursor at the end of the word. ++ textIface->textBeforeOffset(fromIndex, stepSize, &fromIndex, &toIndex); ++ } ++ ++ if (fromIndex < 0 && !forward) { ++ textIface->setCursorPosition(0); ++ return nil; ++ } ++ else if (toIndex < 0 && forward) { ++ textIface->setCursorPosition(charCount); ++ return nil; ++ } ++ else { ++ textIface->setCursorPosition(toIndex); ++ } ++ ++ if (fromIndex >= 0 && toIndex >= 0) { ++ textIface->setSelection(0, fromIndex, toIndex); ++ ++ return [[[UIAccessibilityCustomRotorItemResult alloc] ++ initWithTargetElement:self ++ targetRange:[QUITextRange rangeWithNSRange:NSMakeRange(fromIndex, toIndex)] ++ ] ++ autorelease ++ ]; ++ } ++ return nil; ++ } ++ ]; ++ return [rotor autorelease]; ++ }; ++ return @[ ++ createRotor(QAccessible::TextBoundaryType::CharBoundary), ++ createRotor(QAccessible::TextBoundaryType::WordBoundary), ++ createRotor(QAccessible::TextBoundaryType::SentenceBoundary) ++ ]; ++} ++ + - (NSString*)accessibilityLanguage + { + const QLocale locale; +diff --git x/qtbase/src/plugins/platforms/ios/uistrings.cpp y/qtbase/src/plugins/platforms/ios/uistrings.cpp +index b17438ba93ad2894667d5b7e499d274179c5965b..6b7efe5a60a4c1406b8eeff0a7f0178ff16cf78e 100644 +--- x/qtbase/src/plugins/platforms/ios/uistrings.cpp ++++ y/qtbase/src/plugins/platforms/ios/uistrings.cpp +@@ -12,4 +12,8 @@ const char ACCESSIBILITY_ELEMENT[] = "quiaccessibilityelement"; + const char AE_CHECKED[] = QT_TRANSLATE_NOOP("quiaccessibilityelement", "checked"); + const char AE_UNCHECKED[] = QT_TRANSLATE_NOOP("quiaccessibilityelement", "unchecked"); + ++extern const char A11Y_ROTOR_NAME_CHARS[] = QT_TRANSLATE_NOOP("quiaccessibilityelement", "Character by character"); ++extern const char A11Y_ROTOR_NAME_WORDS[] = QT_TRANSLATE_NOOP("quiaccessibilityelement", "Word by word"); ++extern const char A11Y_ROTOR_NAME_SENTENCES[] = QT_TRANSLATE_NOOP("quiaccessibilityelement", "Sentence by sentence"); ++ + QT_END_NAMESPACE +diff --git x/qtbase/src/plugins/platforms/ios/uistrings_p.h y/qtbase/src/plugins/platforms/ios/uistrings_p.h +index cbe139afaccbffb85aad8894646df6f6527eac17..8eea3baa84ee2526ac80356cc328896f621c18a4 100644 +--- x/qtbase/src/plugins/platforms/ios/uistrings_p.h ++++ y/qtbase/src/plugins/platforms/ios/uistrings_p.h +@@ -25,6 +25,10 @@ extern const char ACCESSIBILITY_ELEMENT[]; + extern const char AE_CHECKED[]; + extern const char AE_UNCHECKED[]; + ++extern const char A11Y_ROTOR_NAME_CHARS[]; ++extern const char A11Y_ROTOR_NAME_WORDS[]; ++extern const char A11Y_ROTOR_NAME_SENTENCES[]; ++ + QT_END_NAMESPACE + + #endif // UISTRINGS_P_H diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0029-Revert-Re-land-Android-destroy-the-window-surface-on.patch ausweisapp2-2.4.0/libs/patches/qtbase-0029-Revert-Re-land-Android-destroy-the-window-surface-on.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0029-Revert-Re-land-Android-destroy-the-window-surface-on.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0029-Revert-Re-land-Android-destroy-the-window-surface-on.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,218 @@ +From 5e61f9e75ea381c7bc680be344ced3bde1abc813 Mon Sep 17 00:00:00 2001 +From: Assam Boudjelthia +Date: Wed, 3 Sep 2025 14:51:47 +0300 +Subject: Revert "Re-land: Android: destroy the window surface only after exit + transition" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reverts commit 5ef73686cfb488dfc6a4077a2ecb12f883de1461. + +Reason for revert: causes multiple regressions. + +Pick-to: 6.9 6.10 +Task-number: QTBUG-127705 +Task-number: QTBUG-139606 +Task-number: QTBUG-139659 +Change-Id: I446d2aad7babf71ed59aa56e29e01b85eb940867 +Reviewed-by: Tor Arne Vestbø +(cherry picked from commit a113197fec0af3698f80679cef5a848426918863) +--- + .../qt/android/QtActivityDelegate.java | 37 +++++++------- + .../org/qtproject/qt/android/QtWindow.java | 50 +------------------ + .../android/qandroidplatformwindow.cpp | 11 ---- + 3 files changed, 18 insertions(+), 80 deletions(-) + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +index 58d332c72812a0fbdf322c3be5dbd1c390e7e1b8..a62dc2095fc3c1930f7bc1fb35d8c401c8755c8f 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +@@ -9,11 +9,9 @@ import android.app.Activity; + import android.content.pm.ActivityInfo; + import android.content.pm.PackageManager; + import android.content.res.Configuration; +-import android.content.res.TypedArray; + import android.graphics.drawable.ColorDrawable; + import android.graphics.drawable.Drawable; + import android.graphics.Rect; +-import android.graphics.Color; + import android.os.Build; + import android.util.DisplayMetrics; + import android.util.Log; +@@ -43,6 +41,7 @@ class QtActivityDelegate extends QtActivityDelegateBase + private boolean m_splashScreenSticky = false; + private boolean m_backendsRegistered = false; + ++ private View m_dummyView = null; + private final HashMap m_nativeViews = new HashMap<>(); + + QtActivityDelegate(Activity activity) +@@ -205,19 +204,6 @@ class QtActivityDelegate extends QtActivityDelegateBase + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + m_layout.addView(m_splashScreen); +- +- // Set DayNight theme as layout background so splash screen +- // is not visible with opaque windows. +- TypedArray typedArray = m_activity.getTheme().obtainStyledAttributes( +- android.R.style.Theme_DeviceDefault_DayNight, +- new int[]{ android.R.attr.colorBackground }); +- try { +- int backgroundColor = typedArray.getColor(0, Color.WHITE); +- Drawable background = new ColorDrawable(backgroundColor); +- m_layout.setBackground(background); +- } finally { +- typedArray.recycle(); +- } + } + } catch (Exception e) { + e.printStackTrace(); +@@ -400,9 +386,15 @@ class QtActivityDelegate extends QtActivityDelegateBase + if (m_layout == null) + return; + ++ if (m_topLevelWindows.isEmpty()) { ++ if (m_dummyView != null) { ++ m_layout.removeView(m_dummyView); ++ m_dummyView = null; ++ } ++ } ++ + m_layout.addView(window, m_topLevelWindows.size()); + m_topLevelWindows.put(window.getId(), window); +- window.setToDestroy(false); + if (!m_splashScreenSticky) + hideSplashScreen(); + }); +@@ -416,10 +408,10 @@ class QtActivityDelegate extends QtActivityDelegateBase + if (m_topLevelWindows.containsKey(id)) { + QtWindow window = m_topLevelWindows.remove(id); + window.setOnApplyWindowInsetsListener(null); // Set in QtWindow for safe margins +- if (window.isFrontmostVisibleWindow()) { +- window.setToDestroy(true); +- // Keep current shown window open during shutdown transition +- m_layout.postDelayed(() -> { window.destroySurface(); }, 500); ++ if (m_topLevelWindows.isEmpty()) { ++ // Keep last frame in stack until it is replaced to get correct ++ // shutdown transition ++ m_dummyView = window; + } else if (m_layout != null) { + m_layout.removeView(window); + } +@@ -478,6 +470,11 @@ class QtActivityDelegate extends QtActivityDelegateBase + return; + + QtNative.runAction(()-> { ++ if (m_dummyView != null) { ++ m_layout.removeView(m_dummyView); ++ m_dummyView = null; ++ } ++ + if (m_nativeViews.containsKey(id)) + m_layout.removeView(m_nativeViews.remove(id)); + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtWindow.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtWindow.java +index 7a3db400f9e7dffd8ebe73b22d69ef77d51a9efd..1f5ae6c2ad95dbab92fd1c33a9a934c4c09c1ce1 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtWindow.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtWindow.java +@@ -20,7 +20,6 @@ import android.view.WindowInsets; + import android.os.Build; + + import java.util.HashMap; +-import java.util.concurrent.atomic.AtomicBoolean; + + @SuppressLint("ViewConstructor") + class QtWindow extends QtLayout implements QtSurfaceInterface { +@@ -31,7 +30,6 @@ class QtWindow extends QtLayout implements QtSurfaceInterface { + private GestureDetector m_gestureDetector; + private final QtEditText m_editText; + private final QtInputConnection.QtInputConnectionListener m_inputConnectionListener; +- private final AtomicBoolean m_canBeDestroyed = new AtomicBoolean(true); + + private static native void setSurface(int windowId, Surface surface); + private static native void safeAreaMarginsChanged(Insets insets, int id); +@@ -164,52 +162,6 @@ class QtWindow extends QtLayout implements QtSurfaceInterface { + }); + } + +- // Use only for Qt for Android and not Qt Quick for Android +- @UsedFromNativeCode +- public void setToDestroy(boolean destroy) +- { +- m_canBeDestroyed.set(destroy); +- } +- // Use only for Qt for Android and not Qt Quick for Android +- private QtLayout getParentContainer() +- { +- if (!(getParent() instanceof QtLayout)) +- return null; +- return (QtLayout) getParent(); +- } +- // Use only for Qt for Android and not Qt Quick for Android +- public boolean isFrontmostVisibleWindow() +- { +- QtLayout parent = getParentContainer(); +- if (getVisibility() != View.VISIBLE || parent == null) +- return false; +- for (int index = parent.indexOfChild(this) + 1; index < parent.getChildCount(); index ++) { +- View child = parent.getChildAt(index); +- if (child instanceof QtWindow && child.isShown()) +- return false; +- } +- return true; +- } +- // Use only for Qt for Android and not Qt Quick for Android +- @UsedFromNativeCode +- public boolean isLastVisibleTopLevelWindow() +- { +- QtLayout parent = getParentContainer(); +- if (getVisibility() != View.VISIBLE || parent == null) +- return false; +- for (int index = parent.indexOfChild(this) - 1; index >= 0; index--) { +- View child = parent.getChildAt(index); +- if (child instanceof QtWindow) { +- QtWindow childWindow = (QtWindow) child; +- if (child.getVisibility() == View.VISIBLE && !childWindow.m_canBeDestroyed.get()) +- return false; +- } +- } +- if (parent.getChildCount() > 1 && !isFrontmostVisibleWindow()) +- return false; +- return true; +- } +- + @Override + public void onSurfaceChanged(Surface surface) + { +@@ -276,7 +228,7 @@ class QtWindow extends QtLayout implements QtSurfaceInterface { + void destroySurface() + { + QtNative.runAction(()-> { +- if (m_surfaceContainer != null && m_canBeDestroyed.get()) { ++ if (m_surfaceContainer != null) { + removeView(m_surfaceContainer); + m_surfaceContainer = null; + } +diff --git x/qtbase/src/plugins/platforms/android/qandroidplatformwindow.cpp y/qtbase/src/plugins/platforms/android/qandroidplatformwindow.cpp +index f20bb2be11c596f3a84726a4f7cc1f8bb13b15fe..e904145a7c91fd96ecf054474677fa6c12fe736c 100644 +--- x/qtbase/src/plugins/platforms/android/qandroidplatformwindow.cpp ++++ y/qtbase/src/plugins/platforms/android/qandroidplatformwindow.cpp +@@ -168,17 +168,6 @@ void QAndroidPlatformWindow::setVisible(bool visible) + { + if (isEmbeddingContainer()) + return; +- +- if (!visible && window()->isTopLevel()) { +- // Do not hide last Qt for Android window. +- // We don't want the splash screen to be shown during the app's +- // exit because it would be the foremost visible screen. +- if (QtAndroid::isQtApplication()) { +- visible = m_nativeQtWindow.callMethod("isLastVisibleTopLevelWindow"); +- m_nativeQtWindow.callMethod("setToDestroy", !visible); +- } +- } +- + m_nativeQtWindow.callMethod("setVisible", visible); + + if (visible) { diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0030-Pass-through-iOS-focusing-events-to-QMacAccessibilit.patch ausweisapp2-2.4.0/libs/patches/qtbase-0030-Pass-through-iOS-focusing-events-to-QMacAccessibilit.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0030-Pass-through-iOS-focusing-events-to-QMacAccessibilit.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0030-Pass-through-iOS-focusing-events-to-QMacAccessibilit.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,57 @@ +From 3cb7f273372fcbefa6e070824828335077d94191 Mon Sep 17 00:00:00 2001 +From: Jens Trillmann +Date: Fri, 22 Aug 2025 09:05:24 +0200 +Subject: Pass through iOS focusing events to QMacAccessibilityElement + +With this change QAccessibility can react to VoiceOver and UIFocus events. +UIFocus is needed for a11y Full Keyboard Access to control via a +keyboard/controller. + +Pick-to: 6.10 +Fixes: QTBUG-139613 +Change-Id: I2f1b6c7d792711a7b9dfa828cee7e66500ad9ce1 +--- + src/plugins/platforms/ios/quiaccessibilityelement.mm | 12 ++++++++++++ + src/plugins/platforms/ios/quiview_accessibility.mm | 8 ++++++++ + 2 files changed, 20 insertions(+) + +diff --git x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +index 98d107b8ed65c3ede133f486eb49094aaac69c68..99bfd4e4a5c1648650845d6a9c2da9b3c1add27e 100644 +--- x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm ++++ y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +@@ -387,6 +387,18 @@ struct RotorNameTranslator { + return locale.bcp47Name().toNSString(); + } + ++- (void)accessibilityElementDidBecomeFocused ++{ ++ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); ++ if (!iface || !iface->isValid()) { ++ qWarning() << "invalid accessible interface for: " << self.axid; ++ return; ++ } ++ ++ auto *actionInterface = iface->actionInterface(); ++ actionInterface->doAction(QAccessibleActionInterface::setFocusAction()); ++} ++ + @end + + #endif +diff --git x/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm y/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm +index 3858c2ca5374b30bf11f0fd4572d79afa0c9c055..da8a0c4df904b63c07afb054d4445a9783153bbf 100644 +--- x/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm ++++ y/qtbase/src/plugins/platforms/ios/quiview_accessibility.mm +@@ -97,4 +97,12 @@ + return NO; + } + ++- (void) didUpdateFocusInContext:(UIFocusUpdateContext *) context ++ withAnimationCoordinator:(UIFocusAnimationCoordinator *) coordinator ++{ ++ if ([context.nextFocusedItem isKindOfClass:[QMacAccessibilityElement class]]) { ++ [(QMacAccessibilityElement*)context.nextFocusedItem accessibilityElementDidBecomeFocused]; ++ } ++} ++ + @end diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0031-Replicate-focusability-of-a11y-elements-for-UIFocus.patch ausweisapp2-2.4.0/libs/patches/qtbase-0031-Replicate-focusability-of-a11y-elements-for-UIFocus.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0031-Replicate-focusability-of-a11y-elements-for-UIFocus.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0031-Replicate-focusability-of-a11y-elements-for-UIFocus.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,34 @@ +From dee1e0cc6954f6f7ca7d8ed5477b7846812aab2a Mon Sep 17 00:00:00 2001 +From: Jens Trillmann +Date: Fri, 22 Aug 2025 09:09:57 +0200 +Subject: Replicate focusability of a11y elements for UIFocus + +When navigating elements using a screen reader all elements with a name/ +description can be focused. When using Full Keyboard Access only +interactive elements can be focused. By making every QMacAccessibilityElement +focusable by the UIFocus API Full Keyboard Access can focus the same elements +as VoiceOver. + +Pick-to: 6.10 +Fixes: QTBUG-139613 +Change-Id: Id3999dc4c3243833c605998209bcba0da5450f4c +--- + src/plugins/platforms/ios/quiaccessibilityelement.mm | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +index 99bfd4e4a5c1648650845d6a9c2da9b3c1add27e..f446e18ac4965e67833a7d6155c0a111213bdbcb 100644 +--- x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm ++++ y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +@@ -399,6 +399,11 @@ struct RotorNameTranslator { + actionInterface->doAction(QAccessibleActionInterface::setFocusAction()); + } + ++- (BOOL)canBecomeFocused ++{ ++ return YES; ++} ++ + @end + + #endif diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0032-Make-nonvisible-vertical-a11y-elements-in-Flickable-.patch ausweisapp2-2.4.0/libs/patches/qtbase-0032-Make-nonvisible-vertical-a11y-elements-in-Flickable-.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0032-Make-nonvisible-vertical-a11y-elements-in-Flickable-.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0032-Make-nonvisible-vertical-a11y-elements-in-Flickable-.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,57 @@ +From 48bfe401bd3c0860c7b8c457595e5141b9ca8e74 Mon Sep 17 00:00:00 2001 +From: Jens Trillmann +Date: Mon, 29 Sep 2025 17:25:38 +0200 +Subject: Make nonvisible vertical a11y elements in Flickable not FKA + accessible + +When navigating an app using FKA elements of Flickables can be selected +by FKA even if they are currently not on screen anymore, e.g. when they +are behind a title bar or a footer. This leads to unintuitive behavior, +as FKA prefers to focus the scrolled invisible elements instead of the +visible title bar or footer elements. +To combat this by only allowing FKA focus on elements that are completely +inside the Flickable rect. We make an exception for horizontal flickables, +as they are needed for e.g. landing page horizontal Flickables. +--- + .../platforms/ios/quiaccessibilityelement.mm | 28 ++++++++++++++++++- + 1 file changed, 27 insertions(+), 1 deletion(-) + +diff --git x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +index f446e18ac4965e67833a7d6155c0a111213bdbcb..47d6ae56779b01af023dcd3c0f0caf6cc0d4ab70 100644 +--- x/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm ++++ y/qtbase/src/plugins/platforms/ios/quiaccessibilityelement.mm +@@ -401,7 +401,33 @@ struct RotorNameTranslator { + + - (BOOL)canBecomeFocused + { +- return YES; ++ QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); ++ if (!iface || !iface->isValid()) { ++ qWarning() << "invalid accessible interface for: " << self.axid; ++ return NO; ++ } ++ QAccessibleInterface *parentIface = iface->parent(); ++ while (parentIface) { ++ if (!parentIface || !parentIface->isValid()) { ++ return YES; ++ } ++ const auto accessibleRole = parentIface->role(); ++ if (accessibleRole == QAccessible::ScrollBar) { ++ break; ++ } ++ parentIface = parentIface->parent(); ++ } ++ ++ if (!parentIface) { ++ return YES; ++ } ++ ++ QRect itemRect = iface->rect(); ++ QRect parentRect = parentIface->rect(); ++ ++ bool strict_vertical_outside = itemRect.y() < parentRect.y() ++ || itemRect.y() + itemRect.height() > parentRect.y() + parentRect.height(); ++ return !strict_vertical_outside; + } + + @end diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0033-Android-a11y-Support-text-selection.patch ausweisapp2-2.4.0/libs/patches/qtbase-0033-Android-a11y-Support-text-selection.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0033-Android-a11y-Support-text-selection.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0033-Android-a11y-Support-text-selection.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,556 @@ +From 5599f108baaa956e9f2548faeba805e48a3dcdd5 Mon Sep 17 00:00:00 2001 +From: Timon Sassor +Date: Tue, 14 Oct 2025 17:26:26 +0200 +Subject: Android a11y Support text selection + +Change-Id: I57d2047ae7230645e625283d7bf6e644bd137ba4 +--- + .../qt/android/QtAccessibilityDelegate.java | 68 ++++- + .../qt/android/QtNativeAccessibility.java | 9 +- + src/gui/accessible/qaccessible.cpp | 5 +- + .../android/androidjniaccessibility.cpp | 272 ++++++++++++++++-- + 4 files changed, 320 insertions(+), 34 deletions(-) + +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +index 81db2da44d866e12c454af04f5e5c1d16ddf6f9b..8739ff96c965fc02471a5b6e5407ab4b5d536646 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +@@ -4,6 +4,8 @@ + + package org.qtproject.qt.android; + ++import android.content.ClipData; ++import android.content.ClipboardManager; + import android.content.Context; + import android.graphics.Rect; + import android.os.Build; +@@ -44,6 +46,7 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + private int m_hoveredVirtualViewId = INVALID_ID; + + private int m_currentRotorSetting = AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER; ++ private int m_lastCursorMovement = AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY; + + // Cache coordinates of the view to know the global offset + // this is because the Android platform window does not take +@@ -388,6 +391,13 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY) { + event.setMovementGranularity(m_currentRotorSetting); + QtNativeAccessibility.populateGranularMovementEvent(virtualViewId, event); ++ event.setAction(m_lastCursorMovement); ++ } ++ ++ if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED) { ++ QtNativeAccessibility.populateSelectionChangedEvent(virtualViewId, event); ++ String name = QtNativeAccessibility.textForAccessibleObject(virtualViewId); ++ event.getText().add(name); + } + + return event; +@@ -514,6 +524,11 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE | + AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH + ); ++ ++ node.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_SELECTION); ++ node.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COPY); ++ node.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CUT); ++ node.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_PASTE); + } + + // Manage internal accessibility focus state. +@@ -621,10 +636,57 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate + AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, + AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER // Default-Value, if not set + ); ++ final boolean extendSelection = arguments.getBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false); + final boolean movingForward = action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY; +- success = QtNativeAccessibility.moveToNextElementAtGranularity(virtualViewId, movingForward, m_currentRotorSetting); +- if (success) +- sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); ++ success = QtNativeAccessibility.moveToNextElementAtGranularity(virtualViewId, movingForward, m_currentRotorSetting, extendSelection); ++ m_lastCursorMovement = action; ++ if (success) { ++ if (extendSelection) { ++ sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); ++ sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); ++ } else { ++ sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY); ++ } ++ } ++ break; ++ case AccessibilityNodeInfo.ACTION_CUT: ++ { ++ ClipboardManager clipboardManager = (ClipboardManager)m_layout.getContext().getSystemService(Context.CLIPBOARD_SERVICE); ++ String text = QtNativeAccessibility.getSelectedText(virtualViewId); ++ success = QtNativeAccessibility.deleteSelectedText(virtualViewId); ++ if (!success) { ++ break; ++ } ++ sendEventForVirtualViewId(virtualViewId, AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); ++ ClipData clipData = ClipData.newPlainText("selected_text", text); ++ clipboardManager.setPrimaryClip(clipData); ++ } ++ break; ++ case AccessibilityNodeInfo.ACTION_COPY: ++ { ++ ClipboardManager clipboardManager = (ClipboardManager)m_layout.getContext().getSystemService(Context.CLIPBOARD_SERVICE); ++ String text = QtNativeAccessibility.getSelectedText(virtualViewId); ++ if (text.isEmpty()) ++ text = QtNativeAccessibility.textForAccessibleObject(virtualViewId); ++ ClipData clipData = ClipData.newPlainText("selected_text", text); ++ clipboardManager.setPrimaryClip(clipData); ++ success = true; ++ } ++ break; ++ case AccessibilityNodeInfo.ACTION_PASTE: ++ { ++ ClipboardManager clipboardManager = (ClipboardManager)m_layout.getContext().getSystemService(Context.CLIPBOARD_SERVICE); ++ if (!clipboardManager.hasPrimaryClip()) { ++ break; ++ } ++ ClipData clipData = clipboardManager.getPrimaryClip(); ++ if (clipData == null || clipData.getItemCount() <= 0) { ++ break; ++ } ++ ClipData.Item item = clipData.getItemAt(0); ++ String text = item.coerceToText(m_layout.getContext()).toString(); ++ success = QtNativeAccessibility.replaceSelectedText(virtualViewId, text); ++ } + break; + } + return success; +diff --git x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java +index 9074e589a779c0bf20c383f0cb0fd40642d49111..49075d92b5528759fa3cfc054a6e4a669bce6246 100644 +--- x/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java ++++ y/qtbase/src/android/jar/src/org/qtproject/qt/android/QtNativeAccessibility.java +@@ -14,6 +14,7 @@ class QtNativeAccessibility + static native int[] childIdListForAccessibleObject(int objectId); + static native int parentId(int objectId); + static native String descriptionForAccessibleObject(int objectId); ++ static native String textForAccessibleObject(int objectId); + static native String appLanguage(); + static native Rect screenRect(int objectId); + static native int hitTest(float x, float y); +@@ -21,9 +22,15 @@ class QtNativeAccessibility + static native boolean focusAction(int objectId); + static native boolean scrollForward(int objectId); + static native boolean scrollBackward(int objectId); +- static native boolean moveToNextElementAtGranularity(int objectId, boolean forward, int granularity); ++ static native boolean moveToNextElementAtGranularity(int objectId, boolean forward, int granularity, boolean extendSelection); ++ static native boolean setTextSelection(int objectId, int start, int end); + + static native boolean populateNode(int objectId, AccessibilityNodeInfo node); + static native boolean populateScrollEvent(int objectId, AccessibilityEvent event); + static native boolean populateGranularMovementEvent(int objectId, AccessibilityEvent event); ++ static native boolean populateSelectionChangedEvent(int objectId, AccessibilityEvent event); ++ ++ static native boolean deleteSelectedText(int objectId); ++ static native String getSelectedText(int objectId); ++ static native boolean replaceSelectedText(int objectId, String newText); + } +diff --git x/qtbase/src/gui/accessible/qaccessible.cpp y/qtbase/src/gui/accessible/qaccessible.cpp +index 919e2cc21fa569ba2d28e6aed2f62966952e8928..3b0a53bab3d9b41a099acea827080ee6cfa6861e 100644 +--- x/qtbase/src/gui/accessible/qaccessible.cpp ++++ y/qtbase/src/gui/accessible/qaccessible.cpp +@@ -2366,10 +2366,7 @@ QString QAccessibleTextInterface::textAtOffset(int offset, QAccessible::TextBoun + offset = txt.size(); + + *startOffset = *endOffset = -1; +- if (txt.isEmpty() || offset < 0 || offset > txt.size()) +- return QString(); +- +- if (offset == txt.size() && boundaryType == QAccessible::CharBoundary) ++ if (txt.isEmpty() || offset < 0 || offset >= txt.size()) + return QString(); + + // type initialized just to silence a compiler warning [-Werror=maybe-uninitialized] +diff --git x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +index d1611aef02fc24f7b6fd38a4e29b2a2fa0079aaa..eb661f82134e1c2cbe46e6250a1b1a302ec87b4c 100644 +--- x/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp ++++ y/qtbase/src/plugins/platforms/android/androidjniaccessibility.cpp +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + static const char m_qtTag[] = "Qt A11Y"; + +@@ -42,6 +43,7 @@ namespace QtAndroidAccessibility + static jmethodID m_setHeadingMethodID = 0; + static jmethodID m_setScrollableMethodID = 0; + static jmethodID m_setTextSelectionMethodID = 0; ++ static jmethodID m_setTextMethodID = 0; + static jmethodID m_setRangeInfoMethodID = 0; + static jmethodID m_setVisibleToUserMethodID = 0; + static jmethodID m_setMaxScrollXMethodID = 0; +@@ -424,7 +426,10 @@ namespace QtAndroidAccessibility + return result && oldPosition != screenRect_helper(firstChildId, false); + } + +- static bool moveToNextElementAtGranularity_helper(int objectId, bool forward, int granularity) ++ static int oldCursorPosition = 0; ++ static int newCursorPosition = 0; ++ ++ static bool moveToNextElementAtGranularity_helper(int objectId, bool forward, int granularity, bool extendSelection) + { + QAccessibleInterface *iface = interfaceFromId(objectId); + if (!iface || !iface->isValid() || !iface->textInterface()) +@@ -436,33 +441,163 @@ namespace QtAndroidAccessibility + const auto movementGranularity = m_movementGranularityMapping.key(granularity, QAccessible::TextBoundaryType::NoBoundary); + const int cursorPosition = textIface->cursorPosition(); + const int charCount = textIface->characterCount(); ++ + const bool wouldMoveOutsideBounds = (!forward && cursorPosition == 0) || (forward && cursorPosition == charCount); + if (movementGranularity == QAccessible::TextBoundaryType::NoBoundary || wouldMoveOutsideBounds) + return false; + +- int fromIndex = 0; +- int toIndex = 0; +- if (forward) ++ int fromIndex = 0, toIndex = 0; ++ int currentSelectionStart = 0, currentSelectionEnd = 0; ++ textIface->selection(0, ¤tSelectionStart, ¤tSelectionEnd); ++ textIface->textAtOffset(cursorPosition, movementGranularity, &fromIndex, &toIndex); ++ if (forward && cursorPosition == toIndex) { + textIface->textAfterOffset(cursorPosition, movementGranularity, &fromIndex, &toIndex); +- else ++ toIndex = qMin(toIndex, charCount); ++ } else if (!forward && cursorPosition == fromIndex) { + textIface->textBeforeOffset(cursorPosition, movementGranularity, &fromIndex, &toIndex); ++ fromIndex = qMax(fromIndex, 0); ++ } else if (!forward && cursorPosition == charCount) { ++ textIface->textAtOffset(cursorPosition - 1, movementGranularity, &fromIndex, &toIndex); ++ fromIndex = qMax(fromIndex, 0); ++ } + +- if (toIndex < 0) +- textIface->setCursorPosition(charCount); +- else if (fromIndex < 0) +- textIface->setCursorPosition(0); +- else +- textIface->setCursorPosition(fromIndex); ++ oldCursorPosition = cursorPosition; ++ ++ const bool cursorBeforeSelection = cursorPosition == currentSelectionStart; ++ if (extendSelection) { ++ if (forward && cursorBeforeSelection) { ++ textIface->setSelection(0, currentSelectionEnd, toIndex); ++ newCursorPosition = toIndex; ++ } else if (forward && !cursorBeforeSelection) { ++ textIface->setSelection(0, currentSelectionStart, toIndex); ++ newCursorPosition = toIndex; ++ } else if (!forward && cursorBeforeSelection) { ++ textIface->setSelection(0, currentSelectionEnd, fromIndex); ++ newCursorPosition = fromIndex; ++ } else if (!forward && !cursorBeforeSelection) { ++ textIface->setSelection(0, currentSelectionStart, fromIndex); ++ newCursorPosition = fromIndex; ++ } ++ } else { ++ textIface->setCursorPosition(forward ? toIndex : fromIndex); ++ newCursorPosition = forward ? toIndex : fromIndex; ++ } + + return true; + } + +- static jboolean moveToNextElementAtGranularity(JNIEnv */*env*/, jobject /*thiz*/, jint objectId, jboolean forward, jint granularity) ++ static jboolean moveToNextElementAtGranularity(JNIEnv */*env*/, jobject /*thiz*/, jint objectId, jboolean forward, jint granularity, jboolean extendSelection) + { + bool result = false; + if (m_accessibilityContext) { +- runInObjectContext(m_accessibilityContext, [objectId, forward, granularity]() { +- return moveToNextElementAtGranularity_helper(objectId, forward, granularity); ++ runInObjectContext(m_accessibilityContext, [objectId, forward, granularity, extendSelection]() { ++ return moveToNextElementAtGranularity_helper(objectId, forward, granularity, extendSelection); ++ }, &result); ++ } ++ return result; ++ } ++ ++ static bool deleteSelectedText_helper(int objectId) { ++ QAccessibleInterface *iface = interfaceFromId(objectId); ++ if (!iface || !iface->isValid() || !iface->textInterface()) ++ return false; ++ ++ QAccessibleTextInterface *textIface = iface->textInterface(); ++ QAccessibleEditableTextInterface *editTextIface = iface->editableTextInterface(); ++ if (textIface && editTextIface && (textIface->selectionCount() > 0)) { ++ int selectionStart = 0, selectionEnd = 0; ++ textIface->selection(0, &selectionStart, &selectionEnd); ++ if (selectionStart < 0 || selectionEnd < 0) { ++ return false; ++ } ++ editTextIface->deleteText(selectionStart, selectionEnd); ++ return true; ++ } ++ ++ return false; ++ } ++ ++ static bool deleteSelectedText(JNIEnv */*env*/, jobject /*thiz*/, jint objectId) { ++ bool result = false; ++ if (m_accessibilityContext) { ++ runInObjectContext(m_accessibilityContext, [objectId]() { ++ return deleteSelectedText_helper(objectId); ++ }, &result); ++ } ++ return result; ++ } ++ ++ static QString getSelectedText_helper(int objectId) { ++ QAccessibleInterface *iface = interfaceFromId(objectId); ++ if (!iface || !iface->isValid() || !iface->textInterface()) ++ return QString(); ++ ++ QAccessibleTextInterface *textIface = iface->textInterface(); ++ if (textIface && (textIface->selectionCount() > 0)) { ++ int selectionStart = 0, selectionEnd = 0; ++ textIface->selection(0, &selectionStart, &selectionEnd); ++ if (selectionStart < 0 || selectionEnd < 0) { ++ return QString(); ++ } ++ return textIface->text(selectionStart, selectionEnd); ++ } ++ ++ return QString(); ++ } ++ ++ static jstring getSelectedText(JNIEnv *env, jobject /*thiz*/, int objectId) { ++ QString text; ++ if (m_accessibilityContext) { ++ runInObjectContext(m_accessibilityContext, [objectId]() { ++ return getSelectedText_helper(objectId); ++ }, &text); ++ } ++ return env->NewString((jchar*) text.constData(), (jsize) text.size()); ++ } ++ ++ ++ static bool replaceSelectedText_helper(int objectId, QString newText) { ++ QAccessibleInterface *iface = interfaceFromId(objectId); ++ if (!iface || !iface->isValid() || !iface->textInterface()) ++ return false; ++ ++ QAccessibleTextInterface *textIface = iface->textInterface(); ++ QAccessibleEditableTextInterface *editTextIface = iface->editableTextInterface(); ++ ++ if (textIface && editTextIface) { ++ if (textIface->selectionCount() > 0) { ++ int selectionStart = 0, selectionEnd = 0; ++ textIface->selection(0, &selectionStart, &selectionEnd); ++ if (selectionStart < 0 || selectionEnd < 0) ++ return false; ++ ++ editTextIface->replaceText(selectionStart, selectionEnd, newText); ++ return true; ++ } else { ++ const int cursorPosition = textIface->cursorPosition(); ++ const int charCount = textIface->characterCount(); ++ if (cursorPosition < 0 || cursorPosition > charCount) ++ return false; ++ ++ editTextIface->insertText(cursorPosition, newText); ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ ++ static bool replaceSelectedText(JNIEnv *env, jobject /*thiz*/, jint objectId, jstring newText) { ++ const char* utfChars = env->GetStringUTFChars(newText, nullptr); ++ if (!utfChars) { ++ return false; ++ } ++ QString convertedText = QString::fromUtf8(utfChars); ++ env->ReleaseStringUTFChars(newText, utfChars); ++ bool result = false; ++ if (m_accessibilityContext) { ++ runInObjectContext(m_accessibilityContext, [objectId, convertedText]() { ++ return replaceSelectedText_helper(objectId, convertedText); + }, &result); + } + return result; +@@ -703,6 +838,24 @@ namespace QtAndroidAccessibility + return env->NewString((jchar*) desc.constData(), (jsize) desc.size()); + } + ++ static QString textForAccessibleObject_helper(int objectId) ++ { ++ QAccessibleInterface *iface = interfaceFromId(objectId); ++ QAccessibleTextInterface *textIface = iface->textInterface(); ++ return textIface->text(0, textIface->characterCount()); ++ } ++ ++ static jstring textForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId) ++ { ++ QString name; ++ if (m_accessibilityContext) { ++ runInObjectContext(m_accessibilityContext, [objectId]() { ++ return textForAccessibleObject_helper(objectId); ++ }, &name); ++ } ++ return env->NewString((jchar*) name.constData(), (jsize) name.size()); ++ } ++ + static jstring appLanguage(JNIEnv *env, jobject /*thiz*/) + { + const QString localeName = QLocale().bcp47Name(); +@@ -718,6 +871,7 @@ namespace QtAndroidAccessibility + QString description; + QString identifier; + bool hasTextSelection = false; ++ QString text; + int selectionStart = 0; + int selectionEnd = 0; + bool hasValue = false; +@@ -739,9 +893,14 @@ namespace QtAndroidAccessibility + info.description = descriptionForInterface(iface); + info.identifier = QAccessibleBridgeUtils::accessibleId(iface); + QAccessibleTextInterface *textIface = iface->textInterface(); +- if (textIface && (textIface->selectionCount() > 0)) { +- info.hasTextSelection = true; +- textIface->selection(0, &info.selectionStart, &info.selectionEnd); ++ if (textIface) { ++ if (textIface->selectionCount() > 0) { ++ info.hasTextSelection = true; ++ textIface->selection(0, &info.selectionStart, &info.selectionEnd); ++ } ++ if (iface->role() == QAccessible::EditableText) { ++ info.text = textIface->text(0, textIface->characterCount()); ++ } + } + QAccessibleValueInterface *valueInterface = iface->valueInterface(); + if (valueInterface) { +@@ -782,6 +941,11 @@ namespace QtAndroidAccessibility + info.actions.contains(QAccessibleActionInterface::decreaseAction()); + const bool scrollableRole = info.role == QAccessible::ScrollBar || info.role == QAccessible::List; + ++ if (!info.text.isEmpty()) { ++ jstring jtext = env->NewString((jchar*)info.text.constData(), (jsize)info.text.size()); ++ env->CallVoidMethod(node, m_setTextMethodID, jtext); ++ } ++ + if (info.hasTextSelection && m_setTextSelectionMethodID) { + env->CallVoidMethod(node, m_setTextSelectionMethodID, info.selectionStart, + info.selectionEnd); +@@ -923,6 +1087,7 @@ namespace QtAndroidAccessibility + bool valid = false; + int fromIndex = 0; + int toIndex = 0; ++ int characterCount = 0; + }; + + static GranularMovementEventInfo populateGranularMovementEvent_helper(int objectId, int movementGranularity) +@@ -940,13 +1105,9 @@ namespace QtAndroidAccessibility + + const auto mappedMovementGranularity = m_movementGranularityMapping.key(movementGranularity, QAccessible::TextBoundaryType::NoBoundary); + if (mappedMovementGranularity != QAccessible::TextBoundaryType::NoBoundary) { +- // Since this method is called after the cursor already moved, we only have to look left of the cursor. +- textIface->textBeforeOffset( +- textIface->cursorPosition(), +- mappedMovementGranularity, +- &info.fromIndex, +- &info.toIndex +- ); ++ info.fromIndex = oldCursorPosition; ++ info.toIndex = newCursorPosition; ++ info.characterCount = abs(info.toIndex - info.fromIndex); + } + + return info; +@@ -969,15 +1130,69 @@ namespace QtAndroidAccessibility + + env->CallVoidMethod(event, m_setFromIndexEventMethodID, info.fromIndex); + env->CallVoidMethod(event, m_setToIndexEventMethodID, info.toIndex); ++ env->CallVoidMethod(event, m_setItemCountMethodID, info.characterCount); + + return true; + } + ++ struct SelectionChangedEventInfo ++ { ++ bool valid = false; ++ int fromIndex = 0; ++ int toIndex = 0; ++ int characterCount = 0; ++ }; ++ ++ static SelectionChangedEventInfo populateSelectionChangedEvent_helper(int objectId) ++ { ++ SelectionChangedEventInfo info; ++ QAccessibleInterface *iface = interfaceFromId(objectId); ++ if (!iface || !iface->isValid()) ++ return info; ++ ++ QAccessibleTextInterface *textIface = iface->textInterface(); ++ if (!textIface) ++ return info; ++ ++ info.valid = true; ++ textIface->selection( ++ 0, ++ &info.fromIndex, ++ &info.toIndex ++ ); ++ info.characterCount = info.toIndex - info.fromIndex; ++ ++ return info; ++ } ++ ++ static jboolean populateSelectionChangedEvent(JNIEnv *env, jobject /*thiz*/, jint objectId, jobject event) ++ { ++ SelectionChangedEventInfo info; ++ if (m_accessibilityContext) { ++ runInObjectContext(m_accessibilityContext, [objectId]() { ++ return populateSelectionChangedEvent_helper(objectId); ++ }, &info); ++ } ++ if (!info.valid) { ++ __android_log_print(ANDROID_LOG_WARN, m_qtTag, "Accessibility: populateSelectionChangedEvent for Invalid ID"); ++ return false; ++ } ++ ++ env->CallVoidMethod(event, m_setFromIndexEventMethodID, info.fromIndex); ++ env->CallVoidMethod(event, m_setToIndexEventMethodID, info.toIndex); ++ env->CallVoidMethod(event, m_setItemCountMethodID, info.characterCount); ++ ++ return true; ++ } ++ ++ ++ + static const JNINativeMethod methods[] = { + {"setActive","(Z)V",(void*)setActive}, + {"childIdListForAccessibleObject", "(I)[I", (jintArray)childIdListForAccessibleObject}, + {"parentId", "(I)I", (void*)parentId}, + {"descriptionForAccessibleObject", "(I)Ljava/lang/String;", (jstring)descriptionForAccessibleObject}, ++ {"textForAccessibleObject", "(I)Ljava/lang/String;", (jstring)textForAccessibleObject}, + {"appLanguage", "()Ljava/lang/String;", (jstring)appLanguage}, + {"screenRect", "(I)Landroid/graphics/Rect;", (jobject)screenRect}, + {"hitTest", "(FF)I", (void*)hitTest}, +@@ -987,8 +1202,12 @@ namespace QtAndroidAccessibility + {"focusAction", "(I)Z", (void*)focusAction}, + {"scrollForward", "(I)Z", (void*)scrollForward}, + {"scrollBackward", "(I)Z", (void*)scrollBackward}, +- {"moveToNextElementAtGranularity", "(IZI)Z", (void *)moveToNextElementAtGranularity}, +- {"populateGranularMovementEvent", "(ILandroid/view/accessibility/AccessibilityEvent;)Z", (void *)populateGranularMovementEvent} ++ {"moveToNextElementAtGranularity", "(IZIZ)Z", (void *)moveToNextElementAtGranularity}, ++ {"populateGranularMovementEvent", "(ILandroid/view/accessibility/AccessibilityEvent;)Z", (void *)populateGranularMovementEvent}, ++ {"populateSelectionChangedEvent", "(ILandroid/view/accessibility/AccessibilityEvent;)Z", (void *)populateSelectionChangedEvent}, ++ {"deleteSelectedText", "(I)Z", (void*)deleteSelectedText}, ++ {"getSelectedText", "(I)Ljava/lang/String;", (jstring)getSelectedText}, ++ {"replaceSelectedText", "(ILjava/lang/String;)Z", (void*)replaceSelectedText} + }; + + #define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ +@@ -1023,6 +1242,7 @@ namespace QtAndroidAccessibility + GET_AND_CHECK_STATIC_METHOD(m_setScrollableMethodID, nodeInfoClass, "setScrollable", "(Z)V"); + GET_AND_CHECK_STATIC_METHOD(m_setVisibleToUserMethodID, nodeInfoClass, "setVisibleToUser", "(Z)V"); + GET_AND_CHECK_STATIC_METHOD(m_setTextSelectionMethodID, nodeInfoClass, "setTextSelection", "(II)V"); ++ GET_AND_CHECK_STATIC_METHOD(m_setTextMethodID, nodeInfoClass, "setText", "(Ljava/lang/CharSequence;)V"); + GET_AND_CHECK_STATIC_METHOD( + m_setRangeInfoMethodID, nodeInfoClass, "setRangeInfo", + "(Landroid/view/accessibility/AccessibilityNodeInfo$RangeInfo;)V"); diff -Nru ausweisapp2-2.3.1/libs/patches/qtbase-0034-Fix-native-a11y-role-of-QAccessible-LayeredPane.patch ausweisapp2-2.4.0/libs/patches/qtbase-0034-Fix-native-a11y-role-of-QAccessible-LayeredPane.patch --- ausweisapp2-2.3.1/libs/patches/qtbase-0034-Fix-native-a11y-role-of-QAccessible-LayeredPane.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtbase-0034-Fix-native-a11y-role-of-QAccessible-LayeredPane.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,43 @@ +From 25108c33d85a13e22b676cb9e5a358e3df44c939 Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Mon, 20 Oct 2025 10:43:52 +0200 +Subject: Fix native a11y role of QAccessible::LayeredPane + +* The QML StackView started to use this role in + qtdeclarative ee7151d5ab29ba812a93c1231ddc59c9129a7af0. +* This will replace "Unknown" with "Window" on Windows with NVDA. +* On macOS the behavior with VoiceOver is not changed but + it seems sensible to supplement the mapping as well. + +Fixes: QTBUG-134138 +Pick-to: 6.10 6.8 +Change-Id: Iab5c7339a82d91b6eafdba45fa31c0537a94a135 +--- + src/plugins/platforms/cocoa/qcocoaaccessibility.mm | 1 + + src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp | 1 + + 2 files changed, 2 insertions(+) + +diff --git x/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm y/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +index 6063795d6b4e9f9ee9d2ad0d431d84e49abd4b75..642d7cf115f400df5a73f7511784df90f6298d67 100644 +--- x/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm ++++ y/qtbase/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +@@ -160,6 +160,7 @@ static void populateRoleMap() + roleMap[QAccessible::Graphic] = NSAccessibilityImageRole; + roleMap[QAccessible::Tree] = NSAccessibilityOutlineRole; + roleMap[QAccessible::BlockQuote] = NSAccessibilityGroupRole; ++ roleMap[QAccessible::LayeredPane] = NSAccessibilityGroupRole; + } + + /* +diff --git x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp +index 268961486e75ea82d26172309051056d7c61369c..5a58759fbd5dcc3a45dd894707b6118506c4ea82 100644 +--- x/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp ++++ y/qtbase/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp +@@ -152,6 +152,7 @@ long roleToControlTypeId(QAccessible::Role role) + {QAccessible::WebDocument, UIA_DocumentControlTypeId}, + {QAccessible::Heading, UIA_TextControlTypeId}, + {QAccessible::BlockQuote, UIA_GroupControlTypeId}, ++ {QAccessible::LayeredPane, UIA_PaneControlTypeId}, + }; + + return mapping.value(role, UIA_CustomControlTypeId); diff -Nru ausweisapp2-2.3.1/libs/patches/qtconnectivity-0001-Increase-NFC-timeout-on-Android.patch ausweisapp2-2.4.0/libs/patches/qtconnectivity-0001-Increase-NFC-timeout-on-Android.patch --- ausweisapp2-2.3.1/libs/patches/qtconnectivity-0001-Increase-NFC-timeout-on-Android.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtconnectivity-0001-Increase-NFC-timeout-on-Android.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -From c7e2123821cd3eb66ee0330567acfa97a24a9b40 Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Tue, 13 Feb 2024 07:34:17 +0100 -Subject: Increase NFC timeout on Android - ---- - src/nfc/qnearfieldtarget_android.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git x/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp y/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp -index 0dcd602b7400bc9ec57fd3a4f0536f014051d5e8..88155fa376496b7f949bd5a6ca0798d8ef871325 100644 ---- x/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp -+++ y/qtconnectivity/src/nfc/qnearfieldtarget_android.cpp -@@ -453,7 +453,7 @@ bool QNearFieldTargetPrivateImpl::connect() - if (connected) - return true; - -- setCommandTimeout(2000); -+ setCommandTimeout(3000); - methodId = env.findMethod(tagTech.objectClass(), "connect"); - if (!methodId) - return false; diff -Nru ausweisapp2-2.3.1/libs/patches/qtconnectivity-0002-iOS-Improve-reporting-of-NFCReaderTransceiveErrorSes.patch ausweisapp2-2.4.0/libs/patches/qtconnectivity-0002-iOS-Improve-reporting-of-NFCReaderTransceiveErrorSes.patch --- ausweisapp2-2.3.1/libs/patches/qtconnectivity-0002-iOS-Improve-reporting-of-NFCReaderTransceiveErrorSes.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtconnectivity-0002-iOS-Improve-reporting-of-NFCReaderTransceiveErrorSes.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,123 +0,0 @@ -From 7e6a864ac764efa73889e34d754a839ddae4a916 Mon Sep 17 00:00:00 2001 -From: Timon Sassor -Date: Fri, 11 Oct 2024 15:49:15 +0200 -Subject: iOS: Improve reporting of NFCReaderTransceiveErrorSessionInvalidated - -In 9163f0081459fd9303aa6c31602f4e1b95e5e1b6 the reporting of unsupported NFC -cards was improved on iOS by emitting QNearFieldTarget::UnsupportedTargetError. -In the meantime, Apple has changed the behavior. Current versions of iOS no -longer use NFCReaderErrorSecurityViolation during connect but use -NFCReaderTransceiveErrorSessionInvalidated or -NFCReaderTransceiveErrorTagNotConnected in sendCommandAPDU. - -Pick-to: 6.9 6.8 -Change-Id: I4fd553ed25718a03de9aecfe4e67501714c78376 -Reviewed-by: Lars Schmertmann -Reviewed-by: Timur Pocheptsov -(cherry picked from commit 1a489deb37b24641b6c4fab2af199c03b1ce6037) ---- - src/nfc/qnearfieldtarget_ios.mm | 30 +++++++++++++++++++++++------- - src/nfc/qnearfieldtarget_ios_p.h | 7 ++++--- - 2 files changed, 27 insertions(+), 10 deletions(-) - -diff --git x/qtconnectivity/src/nfc/qnearfieldtarget_ios.mm y/qtconnectivity/src/nfc/qnearfieldtarget_ios.mm -index f49ad4acb9cd5f68ca17701f4b3a6541a69d9811..334cd54bd3135a4940295c01e6252036eb7d431f 100644 ---- x/qtconnectivity/src/nfc/qnearfieldtarget_ios.mm -+++ y/qtconnectivity/src/nfc/qnearfieldtarget_ios.mm -@@ -19,8 +19,8 @@ QT_BEGIN_NAMESPACE - - Q_APPLICATION_STATIC(ResponseProvider, responseProvider) - --void ResponseProvider::provideResponse(QNearFieldTarget::RequestId requestId, bool success, QByteArray recvBuffer) { -- Q_EMIT responseReceived(requestId, success, recvBuffer); -+void ResponseProvider::provideResponse(QNearFieldTarget::RequestId requestId, QNearFieldTarget::Error error, QByteArray recvBuffer) { -+ Q_EMIT responseReceived(requestId, error, recvBuffer); - } - - void NfcDeleter::operator()(void *obj) -@@ -275,6 +275,7 @@ bool QNearFieldTargetPrivateImpl::connect() - requestInProgress = QNearFieldTarget::RequestId(); - if (errorCode == -1) { - connected = true; -+ justConnected = true; - onExecuteRequest(); - } else { - const auto requestId = queue.dequeue().first; -@@ -428,23 +429,38 @@ void QNearFieldTargetPrivateImpl::onExecuteRequest() - QByteArray recvBuffer = QByteArray::fromNSData(responseData); - recvBuffer += static_cast(sw1); - recvBuffer += static_cast(sw2); -- const bool success = error == nil; -- responseProvider->provideResponse(request.first, success, recvBuffer); -+ auto errorToReport = QNearFieldTarget::NoError; -+ if (error != nil) -+ { -+ switch (error.code) { -+ case NFCReaderError::NFCReaderTransceiveErrorSessionInvalidated: -+ case NFCReaderError::NFCReaderTransceiveErrorTagNotConnected: -+ if (justConnected) { -+ errorToReport = QNearFieldTarget::UnsupportedTargetError; -+ justConnected = false; -+ break; -+ } -+ Q_FALLTHROUGH(); -+ default: -+ errorToReport = QNearFieldTarget::CommandError; -+ } -+ } -+ responseProvider->provideResponse(request.first, errorToReport, recvBuffer); - }]; - } - } - --void QNearFieldTargetPrivateImpl::onResponseReceived(QNearFieldTarget::RequestId requestId, bool success, QByteArray recvBuffer) -+void QNearFieldTargetPrivateImpl::onResponseReceived(QNearFieldTarget::RequestId requestId, QNearFieldTarget::Error error, QByteArray recvBuffer) - { - if (requestInProgress != requestId) - return; - - requestInProgress = QNearFieldTarget::RequestId(); -- if (success) { -+ if (error == QNearFieldTarget::NoError) { - setResponseForRequest(requestId, recvBuffer, true); - onExecuteRequest(); - } else { -- reportError(QNearFieldTarget::CommandError, requestId); -+ reportError(error, requestId); - invalidate(); - } - } -diff --git x/qtconnectivity/src/nfc/qnearfieldtarget_ios_p.h y/qtconnectivity/src/nfc/qnearfieldtarget_ios_p.h -index c0cde0b621c9f64bc14d36a4b469462a88c5b9b0..9c9b1d302ff1e107fbec27f2d2cbaf885608a155 100644 ---- x/qtconnectivity/src/nfc/qnearfieldtarget_ios_p.h -+++ y/qtconnectivity/src/nfc/qnearfieldtarget_ios_p.h -@@ -33,10 +33,10 @@ class ResponseProvider : public QObject - Q_OBJECT - - public: -- void provideResponse(QNearFieldTarget::RequestId requestId, bool success, QByteArray recvBuffer); -+ void provideResponse(QNearFieldTarget::RequestId requestId, QNearFieldTarget::Error error, QByteArray recvBuffer); - - Q_SIGNALS: -- void responseReceived(QNearFieldTarget::RequestId requestId, bool success, QByteArray recvBuffer); -+ void responseReceived(QNearFieldTarget::RequestId requestId, QNearFieldTarget::Error error, QByteArray recvBuffer); - }; - - struct NfcDeleter -@@ -95,6 +95,7 @@ private: - bool hasNDEFMessage = false; - - bool connected = false; -+ bool justConnected = false; - QTimer targetCheckTimer; - QNearFieldTarget::RequestId requestInProgress; - QQueue> queue; -@@ -108,7 +109,7 @@ private Q_SLOTS: - void onTargetCheck(); - void onTargetError(QNearFieldTarget::Error error, const QNearFieldTarget::RequestId &id); - void onExecuteRequest(); -- void onResponseReceived(QNearFieldTarget::RequestId requestId, bool success, QByteArray recvBuffer); -+ void onResponseReceived(QNearFieldTarget::RequestId requestId, QNearFieldTarget::Error error, QByteArray recvBuffer); - // NDEF: - void messageRead(const QNdefMessage &ndefMessage, QNearFieldTarget::RequestId request); - void messageWritten(QNearFieldTarget::RequestId request); diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0001-A11y-Send-a11y-events-on-Accessible.ignored-changes.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0001-A11y-Send-a11y-events-on-Accessible.ignored-changes.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0001-A11y-Send-a11y-events-on-Accessible.ignored-changes.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0001-A11y-Send-a11y-events-on-Accessible.ignored-changes.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -From 1e9a6663c61caf3efc179980fc6c43b4fccf112c Mon Sep 17 00:00:00 2001 -From: Jens Trillmann -Date: Mon, 19 Feb 2024 09:41:13 +0100 -Subject: A11y: Send a11y events on Accessible.ignored changes - -The Accessible.ignored setter does not send a11y events like the setter -in the header STATE_PROPERTY macro and other setter do. This leads to -changes of Accessible.ignored not being registered by screen readers. - -Fixes: QTBUG-122436 -Pick-to: 6.5 6.6 6.7 -Change-Id: I0626b66c91876fa3e6dd23e76c32e8bfe43136bf ---- - src/quick/items/qquickaccessibleattached.cpp | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git x/qtdeclarative/src/quick/items/qquickaccessibleattached.cpp y/qtdeclarative/src/quick/items/qquickaccessibleattached.cpp -index 4ae9247db37d1c1c2b1e3e4a9f993c0b329e1c75..3aab878e04fb0c45295f798f576ab2e390f287ec 100644 ---- x/qtdeclarative/src/quick/items/qquickaccessibleattached.cpp -+++ y/qtdeclarative/src/quick/items/qquickaccessibleattached.cpp -@@ -445,6 +445,8 @@ void QQuickAccessibleAttached::setIgnored(bool ignored) - auto item = qobject_cast(parent()); - if (item && this->ignored() != ignored) { - item->d_func()->isAccessible = !ignored; -+ QAccessibleEvent ev(item, ignored ? QAccessible::ObjectHide : QAccessible::ObjectShow); -+ QAccessible::updateAccessibility(&ev); - emit ignoredChanged(); - } - } diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0001-Basic-Style-Add-support-for-dark-mode-color-scheme.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0001-Basic-Style-Add-support-for-dark-mode-color-scheme.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0001-Basic-Style-Add-support-for-dark-mode-color-scheme.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0001-Basic-Style-Add-support-for-dark-mode-color-scheme.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,198 @@ +From dd16c35412afae1cd7a3c036ddd7f7ec1b9aa40b Mon Sep 17 00:00:00 2001 +From: MohammadHossein Qanbari +Date: Wed, 26 Mar 2025 13:51:11 +0100 +Subject: Basic Style: Add support for dark mode color scheme + +Previously, the basic style did not respect the system's dark mode +settings because the palette initialized by QQuickBasicTheme lacked +definitions for dark mode colors. + +This commit updates the palette initialization to dynamically set colors +based on the current system color scheme. Additionally, the palette is +reinitialized whenever the system color scheme changes. + +The controls gallery example has been updated to demonstrate the new +functionality. + +[ChangeLog][Controls][Basic] Basic style supports dark mode now. + +Fixes: QTBUG-135207 +Change-Id: I2d6a74b407a7981905a9b86e97004cf0609a4bf0 +Reviewed-by: Mitch Curtis +(cherry picked from commit 52141b34ecd16b33e9879fc1047c6f9c3dfde6da) +--- + examples/quickcontrols/gallery/gallery.qml | 2 +- + src/quickcontrols/basic/qquickbasictheme.cpp | 84 +++++++++++++------ + src/quickcontrols/basic/qquickbasictheme_p.h | 1 + + .../qtquickcontrols2basicstyleplugin.cpp | 6 ++ + 4 files changed, 68 insertions(+), 25 deletions(-) + +diff --git x/qtdeclarative/examples/quickcontrols/gallery/gallery.qml y/qtdeclarative/examples/quickcontrols/gallery/gallery.qml +index 15d49fa52e3898cdd607dc6e499beca37645ac25..3481fc9a151ef902df82984dd0f4ad3d144fe368 100644 +--- x/qtdeclarative/examples/quickcontrols/gallery/gallery.qml ++++ y/qtdeclarative/examples/quickcontrols/gallery/gallery.qml +@@ -271,7 +271,7 @@ ApplicationWindow { + id: colorSchemes + // Some Qt Quick styles prioritize the respective design system guidelines + // over the system palette. +- enabled: ["FluentWinUI3", "Fusion", "iOS"].includes(styleBox.currentText) ++ enabled: ["FluentWinUI3", "Fusion", "iOS", "Basic"].includes(styleBox.currentText) + CheckBox { + id: autoColorScheme + checked: true +diff --git x/qtdeclarative/src/quickcontrols/basic/qquickbasictheme.cpp y/qtdeclarative/src/quickcontrols/basic/qquickbasictheme.cpp +index 7884ad102e98072f49b8d7647e06b2248fdebe0f..735b4e95690f176e103e8d104013b225fae55f26 100644 +--- x/qtdeclarative/src/quickcontrols/basic/qquickbasictheme.cpp ++++ y/qtdeclarative/src/quickcontrols/basic/qquickbasictheme.cpp +@@ -4,6 +4,7 @@ + #include "qquickbasictheme_p.h" + + #include ++#include + + QT_BEGIN_NAMESPACE + +@@ -11,48 +12,83 @@ void QQuickBasicTheme::initialize(QQuickTheme *theme) + { + QPalette systemPalette; + +- systemPalette.setColor(QPalette::Base, QColor::fromRgba(0xFFFFFFFF)); +- systemPalette.setColor(QPalette::Disabled, QPalette::Base, QColor::fromRgba(0xFFD6D6D6)); ++ const bool isDarkSystemTheme = QQuickStylePrivate::isDarkSystemTheme(); + +- systemPalette.setColor(QPalette::Button, QColor::fromRgba(0xFFE0E0E0)); ++ const QRgb base(isDarkSystemTheme ? 0xFF000000 : 0xFFFFFFFF); ++ const QRgb disabledBase(isDarkSystemTheme ? 0xFF292929 : 0xFFD6D6D6); ++ const QRgb button(isDarkSystemTheme ? 0xFF2F2F2F : 0xFFE0E0E0); ++ const QRgb buttonText(isDarkSystemTheme ? 0xFFD4D6D8 : 0xFF26282A); ++ const QRgb disabledButtonText(isDarkSystemTheme ? 0x4DD4D6D8 : 0x4D26282A); ++ const QRgb brightText(isDarkSystemTheme ? 0xFF000000 : 0xFFFFFFFF); ++ const QRgb disabledBrightText(isDarkSystemTheme ? 0x4D000000 : 0x4DFFFFFF); ++ const QRgb dark(isDarkSystemTheme ? 0xFFC8C9CB : 0xFF353637); ++ const QRgb highlight(isDarkSystemTheme ? 0xFF0D69F2 : 0xFF0066FF); ++ const QRgb disabledHighlight(isDarkSystemTheme ? 0xFF01060F : 0xFFF0F6FF); ++ const QRgb highlightedText(isDarkSystemTheme ? 0xFFFDFDFD : 0xFF090909); ++ const QRgb light(isDarkSystemTheme ? 0xFF1A1A1A : 0xFFF6F6F6); ++ const QRgb link(isDarkSystemTheme ? 0xFF2F86B1 : 0xFF45A7D7); ++ const QRgb mid(isDarkSystemTheme ? 0xFF626262 : 0xFFBDBDBD); ++ const QRgb midlight(isDarkSystemTheme ? 0xFF2C2C2C : 0xFFE4E4E4); ++ const QRgb text(isDarkSystemTheme ? 0xFFEFF0F2 : 0xFF353637); ++ const QRgb disabledText(isDarkSystemTheme ? 0x7FC8C9CB : 0x7F353637); ++ const QRgb shadow(isDarkSystemTheme ? 0xFF28282A : 0xFF28282A); ++ const QRgb toolTipBase(isDarkSystemTheme ? 0xFF000000 : 0xFFFFFFFF); ++ const QRgb toolTipText(isDarkSystemTheme ? 0xFFFFFFFF : 0xFF000000); ++ const QRgb window(isDarkSystemTheme ? 0xFF000000 : 0xFFFFFFFF); ++ const QRgb windowText(isDarkSystemTheme ? 0xFFD4D6D8 : 0xFF26282A); ++ const QRgb disabledWindowText(isDarkSystemTheme ? 0xFF3F4040 : 0xFFBDBEBF); ++ const QRgb placeholderText(isDarkSystemTheme ? 0x88C8C9CB : 0x88353637); + +- systemPalette.setColor(QPalette::ButtonText, QColor::fromRgba(0xFF26282A)); +- systemPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor::fromRgba(0x4D26282A)); ++ systemPalette.setColor(QPalette::Base, base); ++ systemPalette.setColor(QPalette::Disabled, QPalette::Base, disabledBase); + +- systemPalette.setColor(QPalette::BrightText, QColor::fromRgba(0xFFFFFFFF)); +- systemPalette.setColor(QPalette::Disabled, QPalette::BrightText, QColor::fromRgba(0x4DFFFFFF)); ++ systemPalette.setColor(QPalette::Button, button); + +- systemPalette.setColor(QPalette::Dark, QColor::fromRgba(0xFF353637)); ++ systemPalette.setColor(QPalette::ButtonText, buttonText); ++ systemPalette.setColor(QPalette::Disabled, QPalette::ButtonText, disabledButtonText); + +- systemPalette.setColor(QPalette::Highlight, QColor::fromRgba(0xFF0066FF)); +- systemPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor::fromRgba(0xFFF0F6FF)); ++ systemPalette.setColor(QPalette::BrightText, brightText); ++ systemPalette.setColor(QPalette::Disabled, QPalette::BrightText, disabledBrightText); + +- systemPalette.setColor(QPalette::HighlightedText, QColor::fromRgba(0xFF090909)); ++ systemPalette.setColor(QPalette::Dark, dark); + +- systemPalette.setColor(QPalette::Light, QColor::fromRgba(0xFFF6F6F6)); ++ systemPalette.setColor(QPalette::Highlight, highlight); ++ systemPalette.setColor(QPalette::Disabled, QPalette::Highlight, disabledHighlight); + +- systemPalette.setColor(QPalette::Link, QColor::fromRgba(0xFF45A7D7)); ++ systemPalette.setColor(QPalette::HighlightedText, highlightedText); + +- systemPalette.setColor(QPalette::Mid, QColor::fromRgba(0xFFBDBDBD)); ++ systemPalette.setColor(QPalette::Light, light); + +- systemPalette.setColor(QPalette::Midlight, QColor::fromRgba(0xFFE4E4E4)); ++ systemPalette.setColor(QPalette::Link, link); + +- systemPalette.setColor(QPalette::Text, QColor::fromRgba(0xFF353637)); +- systemPalette.setColor(QPalette::Disabled, QPalette::Text, QColor::fromRgba(0x7F353637)); ++ systemPalette.setColor(QPalette::Mid, mid); + +- systemPalette.setColor(QPalette::Shadow, QColor::fromRgba(0xFF28282A)); ++ systemPalette.setColor(QPalette::Midlight, midlight); + +- systemPalette.setColor(QPalette::ToolTipBase, QColor::fromRgba(0xFFFFFFFF)); +- systemPalette.setColor(QPalette::ToolTipText, QColor::fromRgba(0xFF000000)); ++ systemPalette.setColor(QPalette::Text, text); ++ systemPalette.setColor(QPalette::Disabled, QPalette::Text, disabledText); + +- systemPalette.setColor(QPalette::Window, QColor::fromRgba(0xFFFFFFFF)); ++ systemPalette.setColor(QPalette::Shadow, shadow); + +- systemPalette.setColor(QPalette::WindowText, QColor::fromRgba(0xFF26282A)); +- systemPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor::fromRgba(0xFFBDBEBF)); ++ systemPalette.setColor(QPalette::ToolTipBase, toolTipBase); ++ systemPalette.setColor(QPalette::ToolTipText, toolTipText); + +- systemPalette.setColor(QPalette::PlaceholderText, QColor::fromRgba(0x88353637)); ++ systemPalette.setColor(QPalette::Window, window); ++ ++ systemPalette.setColor(QPalette::WindowText, windowText); ++ systemPalette.setColor(QPalette::Disabled, QPalette::WindowText, disabledWindowText); ++ ++ systemPalette.setColor(QPalette::PlaceholderText, placeholderText); + + theme->setPalette(QQuickTheme::System, systemPalette); + } + ++void QQuickBasicTheme::updateTheme() ++{ ++ QQuickTheme *theme = QQuickTheme::instance(); ++ if (!theme) ++ return; ++ initialize(theme); ++} ++ + QT_END_NAMESPACE +diff --git x/qtdeclarative/src/quickcontrols/basic/qquickbasictheme_p.h y/qtdeclarative/src/quickcontrols/basic/qquickbasictheme_p.h +index bcedee2a12afdee2715ab01fd9d62d3f8e594beb..33a32afd9cc91433e14678e84099071bfe85bb0d 100644 +--- x/qtdeclarative/src/quickcontrols/basic/qquickbasictheme_p.h ++++ y/qtdeclarative/src/quickcontrols/basic/qquickbasictheme_p.h +@@ -25,6 +25,7 @@ class Q_QUICKCONTROLS2BASIC_EXPORT QQuickBasicTheme + { + public: + static void initialize(QQuickTheme *theme); ++ static void updateTheme(); + }; + + QT_END_NAMESPACE +diff --git x/qtdeclarative/src/quickcontrols/basic/qtquickcontrols2basicstyleplugin.cpp y/qtdeclarative/src/quickcontrols/basic/qtquickcontrols2basicstyleplugin.cpp +index e2d726082b49b15ffc90b0012934dd45dfcfa00a..274891dc2ab3c952a50bc7c06bf26b12f8f627f5 100644 +--- x/qtdeclarative/src/quickcontrols/basic/qtquickcontrols2basicstyleplugin.cpp ++++ y/qtdeclarative/src/quickcontrols/basic/qtquickcontrols2basicstyleplugin.cpp +@@ -21,6 +21,7 @@ public: + + QString name() const override; + void initializeTheme(QQuickTheme *theme) override; ++ void updateTheme() override; + + QQuickBasicTheme theme; + }; +@@ -41,6 +42,11 @@ void QtQuickControls2BasicStylePlugin::initializeTheme(QQuickTheme *theme) + this->theme.initialize(theme); + } + ++void QtQuickControls2BasicStylePlugin::updateTheme() ++{ ++ this->theme.updateTheme(); ++} ++ + QT_END_NAMESPACE + + #include "qtquickcontrols2basicstyleplugin.moc" diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0002-Implement-minimum-maximum-value-also-for-to-from-pro.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0002-Implement-minimum-maximum-value-also-for-to-from-pro.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0002-Implement-minimum-maximum-value-also-for-to-from-pro.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0002-Implement-minimum-maximum-value-also-for-to-from-pro.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,190 @@ +From 9e7c811ae4a53fdfc7dbd2752e03d214b140222e Mon Sep 17 00:00:00 2001 +From: Even Oscar Andersen +Date: Tue, 4 Mar 2025 13:52:05 +0100 +Subject: Implement minimum/maximum-value also for to/from properties +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some objects have to,from properties instead of minimumValue, +maximumValue properties. + +Switch to to,from if minimum,maximum-Value does not exist + +Change-Id: Ibb8e2ae3214c5fe654420a5b338d66e8fdae8c6e +Reviewed-by: Jan Arve Sæther +(cherry picked from commit 3b6701011b6889bc9e1d0863775272098ccdc58a) +--- + src/quick/accessible/qaccessiblequickitem.cpp | 26 +++++++++- + .../quickcontrols/accessibility/data/item.qml | 52 +++++++++++++++++++ + .../accessibility/tst_accessibility.cpp | 50 ++++++++++++++++++ + 3 files changed, 126 insertions(+), 2 deletions(-) + create mode 100644 tests/auto/quickcontrols/accessibility/data/item.qml + +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +index 494ada3e5d7a51f827a242b8397eee49509ee9bd..c0f6b42425198af7aac30040c28d4e1a230c96c6 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +@@ -712,12 +712,34 @@ void QAccessibleQuickItem::setCurrentValue(const QVariant &value) + + QVariant QAccessibleQuickItem::maximumValue() const + { +- return item()->property("maximumValue"); ++ const auto minimumValue = item()->property("minimumValue"); ++ const auto maximumValue = item()->property("maximumValue"); ++ const auto from = item()->property("from"); ++ const auto to = item()->property("to"); ++ ++ if (minimumValue.isValid() && maximumValue.isValid()) ++ return maximumValue; ++ ++ if (from.isValid() && to.isValid()) ++ return to; ++ ++ return QVariant(); + } + + QVariant QAccessibleQuickItem::minimumValue() const + { +- return item()->property("minimumValue"); ++ const auto minimumValue = item()->property("minimumValue"); ++ const auto maximumValue = item()->property("maximumValue"); ++ const auto from = item()->property("from"); ++ const auto to = item()->property("to"); ++ ++ if (minimumValue.isValid() && maximumValue.isValid()) ++ return minimumValue; ++ ++ if (from.isValid() && to.isValid()) ++ return from; ++ ++ return QVariant(); + } + + QVariant QAccessibleQuickItem::minimumStepSize() const +diff --git x/qtdeclarative/tests/auto/quickcontrols/accessibility/data/item.qml y/qtdeclarative/tests/auto/quickcontrols/accessibility/data/item.qml +new file mode 100644 +index 0000000000000000000000000000000000000000..800843b0cf74c1ed3339617272804d40852516da +--- /dev/null ++++ y/qtdeclarative/tests/auto/quickcontrols/accessibility/data/item.qml +@@ -0,0 +1,52 @@ ++// Copyright (C) 2025 The Qt Company Ltd. ++// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only ++ ++import QtQuick ++import QtQuick.Controls ++Item { ++ Accessible.role: Accessible.Client ++ Item { ++ id: item1 ++ property double stepSize: 0 ++ property double from: 0 ++ property double to: 100 ++ property double value: 25 ++ Accessible.role: Accessible.Slider ++ Accessible.onDecreaseAction: { value -= stepSize; } ++ Accessible.onIncreaseAction: { value += stepSize; } ++ } ++ Item { ++ id: item2 ++ property double stepSize: 5 ++ property double from: 0 ++ property double to: 100 ++ property double value: 25 ++ Accessible.role: Accessible.Slider ++ Accessible.onDecreaseAction: { value -= stepSize; } ++ Accessible.onIncreaseAction: { value += stepSize; } ++ } ++ Item { ++ id: item3 ++ property double stepSize: 0 ++ property double minimumValue: 0 ++ property double maximumValue: 100 ++ property double from: -1000 ++ property double to: 1000 ++ property double value: 25 ++ Accessible.role: Accessible.Slider ++ Accessible.onDecreaseAction: { value -= stepSize; } ++ Accessible.onIncreaseAction: { value += stepSize; } ++ } ++ Item { ++ id: item4 ++ property double stepSize: 5 ++ property double minimumValue: 0 ++ property double maximumValue: 100 ++ property double from: -1000 ++ property double to: 1000 ++ property double value: 25 ++ Accessible.role: Accessible.Slider ++ Accessible.onDecreaseAction: { value -= stepSize; } ++ Accessible.onIncreaseAction: { value += stepSize; } ++ } ++} +diff --git x/qtdeclarative/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp y/qtdeclarative/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp +index e2eb3d7f8be02de1fbedcbe2b27a81d2a9d53fe7..c7474a73f1eb22f5060505e55b10e4148397ae7b 100644 +--- x/qtdeclarative/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp ++++ y/qtdeclarative/tests/auto/quickcontrols/accessibility/tst_accessibility.cpp +@@ -36,6 +36,7 @@ private slots: + void actionAccessibilityImplicitName(); + + void accessibleName(); ++ void sliderTest(); + private: + QQmlEngine engine; + }; +@@ -376,6 +377,55 @@ void tst_accessibility::accessibleName() + #endif + } + ++void tst_accessibility::sliderTest() ++{ ++#if QT_CONFIG(accessibility) ++ if (!QAccessible::isActive()) { ++ QPlatformAccessibility *accessibility = platformAccessibility(); ++ if (!accessibility) ++ QSKIP("No QPlatformAccessibility available."); ++ accessibility->setActive(true); ++ } ++ ++ QQmlComponent component(&engine); ++ component.loadUrl(testFileUrl("item.qml")); ++ QScopedPointer object(component.create()); ++ QVERIFY2(!object.isNull(), qPrintable(component.errorString())); ++ ++ auto root = QAccessible::queryAccessibleInterface(object.get()); ++ QVERIFY(root); ++ QCOMPARE(root->childCount(), 4); ++ ++ for (int childIndex = 0; childIndex < 4; ++childIndex) ++ { ++ auto item = root->child(childIndex); ++ auto actionIface = item->actionInterface(); ++ QVERIFY(actionIface); ++ auto valueIface = item->valueInterface(); ++ QVERIFY(valueIface); ++ ++ QVERIFY(actionIface->actionNames().contains(QAccessibleActionInterface::increaseAction())); ++ QVERIFY(actionIface->actionNames().contains(QAccessibleActionInterface::decreaseAction())); ++ QCOMPARE(valueIface->currentValue(), 25); ++ QCOMPARE(valueIface->minimumValue(), 0); ++ QCOMPARE(valueIface->maximumValue(), 100); ++ ++ valueIface->setCurrentValue(30); ++ QCOMPARE(valueIface->currentValue(), 30); ++ ++ const auto stepSize = valueIface->minimumStepSize(); ++ ++ actionIface->doAction(QAccessibleActionInterface::increaseAction()); ++ QCOMPARE(valueIface->currentValue(), 30 + stepSize.toDouble()); ++ ++ actionIface->doAction(QAccessibleActionInterface::decreaseAction()); ++ QCOMPARE(valueIface->currentValue(), 30); ++ QCOMPARE(valueIface->minimumValue(), 0); ++ QCOMPARE(valueIface->maximumValue(), 100); ++ } ++#endif ++} ++ + QTEST_MAIN(tst_accessibility) + + #include "tst_accessibility.moc" diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0002-Use-MAP_JIT-on-all-Apple-platforms.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0002-Use-MAP_JIT-on-all-Apple-platforms.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0002-Use-MAP_JIT-on-all-Apple-platforms.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0002-Use-MAP_JIT-on-all-Apple-platforms.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -From 2cfb4112a579b22a60fe43349da6cd8112dc6dd0 Mon Sep 17 00:00:00 2001 -From: Ulf Hermann -Date: Tue, 10 Dec 2024 14:01:21 +0100 -Subject: Use MAP_JIT on all Apple platforms - -... and make it actually compile. - -... and set the flag for JIT pages being prepared. We are clever enough -not to make them executable right away. - -Pick-to: 6.9 6.8 -Task-number: QTBUG-131957 -Change-Id: I07dc6a9c8ec7d0881d73fc5bd44a6059f66dd2eb -Reviewed-by: Fabian Kosmale -(cherry picked from commit caf7f29f3acad624aedc5847819e6e2d28a408cb) ---- - src/3rdparty/masm/wtf/OSAllocatorPosix.cpp | 9 ++++----- - 1 file changed, 4 insertions(+), 5 deletions(-) - -diff --git x/qtdeclarative/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp y/qtdeclarative/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp -index b6ac8ebe91963b4044a4c69c0636df243458d06e..a319a99d7432845b179f9d74c589dcc900112246 100644 ---- x/qtdeclarative/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp -+++ y/qtdeclarative/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp -@@ -138,8 +138,8 @@ void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bo - protection |= PROT_EXEC; - - int flags = MAP_PRIVATE | MAP_ANON; --#if PLATFORM(IOS) -- if (executable) -+#if OS(DARWIN) -+ if (executable || usage == OSAllocator::JSJITCodePages) - flags |= MAP_JIT; - #endif - -@@ -274,9 +274,8 @@ void OSAllocator::releaseDecommitted(void* address, size_t bytes) - bool OSAllocator::canAllocateExecutableMemory() - { - int flags = MAP_PRIVATE; --#if PLATFORM(IOS) -- if (executable) -- flags |= MAP_JIT; -+#if OS(DARWIN) -+ flags |= MAP_JIT; - #endif - - // Get a read/write memfd page diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0003-Quick-Remove-revision-from-Accessible.id.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0003-Quick-Remove-revision-from-Accessible.id.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0003-Quick-Remove-revision-from-Accessible.id.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0003-Quick-Remove-revision-from-Accessible.id.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -From f73f23956068ab954c63518ebe8f40a13fb2e4f1 Mon Sep 17 00:00:00 2001 -From: Ulf Hermann -Date: Mon, 3 Mar 2025 15:20:23 +0100 -Subject: Quick: Remove revision from Accessible.id - -We don't need revisions on attached properties, they are always -qualified anyway. - -Amends commit 94d736fa94d62632e065e9b919413090f3454546 - -Pick-to: 6.8 6.9 6.9.0 -Task-number: QTBUG-134269 -Change-Id: I3a133e0391f639dacc7455804be0558c1dd377bf -Reviewed-by: Fabian Kosmale -(cherry picked from commit 572bea2c515b98806da0be4c16a4357da5c2c4f3) ---- - src/quick/items/qquickaccessibleattached_p.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git x/qtdeclarative/src/quick/items/qquickaccessibleattached_p.h y/qtdeclarative/src/quick/items/qquickaccessibleattached_p.h -index 2d44000242ec50dd763001dc089dc988f4fba068..9f3883b8b7cd32cc18f211ccdeaa8fdf68d00c3b 100644 ---- x/qtdeclarative/src/quick/items/qquickaccessibleattached_p.h -+++ y/qtdeclarative/src/quick/items/qquickaccessibleattached_p.h -@@ -53,7 +53,7 @@ class Q_QUICK_EXPORT QQuickAccessibleAttached : public QObject - Q_PROPERTY(QAccessible::Role role READ role WRITE setRole NOTIFY roleChanged FINAL) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) - Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged FINAL) -- Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged REVISION(6, 8) FINAL) -+ Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged FINAL) - Q_PROPERTY(bool ignored READ ignored WRITE setIgnored NOTIFY ignoredChanged FINAL) - - QML_NAMED_ELEMENT(Accessible) diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0003-a11y-Enable-the-ValueInterface-for-the-ProgressBar.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0003-a11y-Enable-the-ValueInterface-for-the-ProgressBar.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0003-a11y-Enable-the-ValueInterface-for-the-ProgressBar.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0003-a11y-Enable-the-ValueInterface-for-the-ProgressBar.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,31 @@ +From 07f75786b7ec9c4aa4731d85471fd231a46cfd93 Mon Sep 17 00:00:00 2001 +From: Julian Greilich +Date: Wed, 3 Sep 2025 14:30:12 +0200 +Subject: a11y: Enable the ValueInterface for the ProgressBar + +Enabling the ValueInterface is necessary to use that information +for a better a11y support of the ProgressBar. + +Task-number: QTBUG-139712 +Pick-to: 6.8 6.9 6.10 +Change-Id: Ia5e6c1ba4f6cada8e02b7ed4123454302b494b5d +Reviewed-by: Volker Hilsheimer +(cherry picked from commit 775b4abc5f5b676ae377fd47adfe4712e7a54e18) +--- + src/quick/accessible/qaccessiblequickitem.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +index c0f6b42425198af7aac30040c28d4e1a230c96c6..88e0b25ca51af9f0c99b945c91c034cb5ad09559 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +@@ -688,7 +688,8 @@ void *QAccessibleQuickItem::interface_cast(QAccessible::InterfaceType t) + (r == QAccessible::Slider || + r == QAccessible::SpinBox || + r == QAccessible::Dial || +- r == QAccessible::ScrollBar)) ++ r == QAccessible::ScrollBar || ++ r == QAccessible::ProgressBar)) + return static_cast(this); + + if (t == QAccessible::TextInterface) { diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0004-Implement-QAccessibleScrollInterface-in-QAccessibleQ.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0004-Implement-QAccessibleScrollInterface-in-QAccessibleQ.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0004-Implement-QAccessibleScrollInterface-in-QAccessibleQ.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0004-Implement-QAccessibleScrollInterface-in-QAccessibleQ.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,146 @@ +From 0d867d4a0f10e06b762213f40b63f53eff1f64ee Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Thu, 4 Sep 2025 09:00:01 +0200 +Subject: Implement QAccessibleScrollInterface in QAccessibleQuickItem + +Task-number: QTBUG-139833 +Change-Id: I2b81a930d22e198b5438affc126c85c9383b7b46 +--- + src/quick/accessible/qaccessiblequickitem.cpp | 77 +++++++++++++++++++ + src/quick/accessible/qaccessiblequickitem_p.h | 14 +++- + 2 files changed, 90 insertions(+), 1 deletion(-) + +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +index 88e0b25ca51af9f0c99b945c91c034cb5ad09559..451a224af9004b8a48ca25254fbf4e08233f92e9 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +@@ -9,6 +9,8 @@ + #include "QtQuick/private/qquicktext_p.h" + #include + ++#include "QtQuick/private/qquickflickable_p.h" ++#include "QtQuick/private/qquicklistview_p.h" + #include "QtQuick/private/qquicktextinput_p.h" + #include "QtQuick/private/qquickaccessibleattached_p.h" + #include "QtQuick/qquicktextdocument.h" +@@ -698,6 +700,10 @@ void *QAccessibleQuickItem::interface_cast(QAccessible::InterfaceType t) + return static_cast(this); + } + ++ if (t == QAccessible::ScrollInterface ++ && (r == QAccessible::ScrollBar || r == QAccessible::List)) ++ return static_cast(this); ++ + return QAccessibleObject::interface_cast(t); + } + +@@ -748,6 +754,77 @@ QVariant QAccessibleQuickItem::minimumStepSize() const + return item()->property("stepSize"); + } + ++qreal QAccessibleQuickItem::xPosition() const ++{ ++ if (auto *flickable = qobject_cast(item())) ++ return qMax(0.0, flickable->contentX()); ++ ++ return 0.0; ++} ++ ++qreal QAccessibleQuickItem::maximumXPosition() const ++{ ++ if (auto *flickable = qobject_cast(item())) ++ return flickable->contentWidth(); ++ ++ return 0.0; ++} ++ ++qreal QAccessibleQuickItem::yPosition() const ++{ ++ if (auto *flickable = qobject_cast(item())) ++ return qMax(0.0, flickable->contentY()); ++ ++ return 0.0; ++} ++ ++qreal QAccessibleQuickItem::maximumYPosition() const ++{ ++ if (auto *flickable = qobject_cast(item())) ++ return flickable->contentHeight(); ++ ++ return 0.0; ++} ++ ++int QAccessibleQuickItem::itemCount() const ++{ ++ if (auto *listView = qobject_cast(item())) ++ return listView->count(); ++ ++ return -1; ++} ++ ++int QAccessibleQuickItem::fromIndex() const ++{ ++ if (auto *listView = qobject_cast(item())) { ++ // Account for overshooting and dragging over bounds ++ if (listView->orientation() == QQuickListView::Horizontal) ++ return qMax(0, listView->indexAt(listView->contentX(), 0)); ++ else ++ return qMax(0, listView->indexAt(0, listView->contentY())); ++ } ++ ++ return -1; ++} ++ ++int QAccessibleQuickItem::toIndex() const ++{ ++ if (auto *listView = qobject_cast(item())) { ++ // Account for overshooting, dragging over bounds, and rounding errors ++ if (listView->orientation() == QQuickListView::Horizontal) { ++ const qreal x = ++ qMin(listView->contentWidth(), listView->width() + listView->contentX()); ++ return listView->indexAt(x - 1, 0); ++ } else { ++ const qreal y = ++ qMin(listView->contentHeight(), listView->height() + listView->contentY()); ++ return listView->indexAt(0, y - 1); ++ } ++ } ++ ++ return -1; ++} ++ + /*! + \internal + Shared between QAccessibleQuickItem and QAccessibleQuickView +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequickitem_p.h y/qtdeclarative/src/quick/accessible/qaccessiblequickitem_p.h +index 50fd09a5c03d201f3a8baffbbdfa144dbf3ed656..33b4e6476c8c093767f8b4b07621988b3a1f59e3 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequickitem_p.h ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequickitem_p.h +@@ -26,7 +26,11 @@ QT_BEGIN_NAMESPACE + + class QTextDocument; + +-class Q_QUICK_EXPORT QAccessibleQuickItem : public QAccessibleObject, public QAccessibleActionInterface, public QAccessibleValueInterface, public QAccessibleTextInterface ++class Q_QUICK_EXPORT QAccessibleQuickItem : public QAccessibleObject, ++ public QAccessibleActionInterface, ++ public QAccessibleValueInterface, ++ public QAccessibleTextInterface, ++ public QAccessibleScrollInterface + { + public: + QAccessibleQuickItem(QQuickItem *item); +@@ -64,6 +68,14 @@ public: + QVariant minimumValue() const override; + QVariant minimumStepSize() const override; + ++ // Scroll Interface ++ qreal xPosition() const override; ++ qreal maximumXPosition() const override; ++ qreal yPosition() const override; ++ qreal maximumYPosition() const override; ++ int itemCount() const override; ++ int fromIndex() const override; ++ int toIndex() const override; + + // Text Interface + void selection(int selectionIndex, int *startOffset, int *endOffset) const override; diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0004-Revert-Layouts-Add-revision-to-useDefaultSizePolicy.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0004-Revert-Layouts-Add-revision-to-useDefaultSizePolicy.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0004-Revert-Layouts-Add-revision-to-useDefaultSizePolicy.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0004-Revert-Layouts-Add-revision-to-useDefaultSizePolicy.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -From 6f3fe68a253bcdb108b73bd4c63348ab2c054872 Mon Sep 17 00:00:00 2001 -From: Fabian Kosmale -Date: Mon, 3 Mar 2025 14:09:22 +0000 -Subject: Revert "Layouts: Add revision to useDefaultSizePolicy" - -This reverts commit ed40975bf5b4a30ab0c8fe05f8ee19e9f3db3bc3. - -Reason for revert: We don't need revisions on attached properties, -they are always qualified anyway. - -Change-Id: Id717827467b1974d92f2cf620bf44bf12523bae9 -Task-number: QTBUG-134269 -Pick-to: 6.8 6.9 6.9.0 -Reviewed-by: Ulf Hermann -(cherry picked from commit 33c3367510574a8bb5581288d27e758c886a8d41) ---- - src/quicklayouts/qquicklayout_p.h | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git x/qtdeclarative/src/quicklayouts/qquicklayout_p.h y/qtdeclarative/src/quicklayouts/qquicklayout_p.h -index 22d7a259aa6bed01d08dad23fa0da8e931c8c70d..2f29c783d5a4765b18de9c797e53f061f29d3cb8 100644 ---- x/qtdeclarative/src/quicklayouts/qquicklayout_p.h -+++ y/qtdeclarative/src/quicklayouts/qquicklayout_p.h -@@ -174,7 +174,7 @@ class Q_QUICKLAYOUTS_EXPORT QQuickLayoutAttached : public QObject - Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged FINAL) - Q_PROPERTY(bool fillHeight READ fillHeight WRITE setFillHeight NOTIFY fillHeightChanged FINAL) - Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged FINAL) -- Q_PROPERTY(QQuickLayout::SizePolicy useDefaultSizePolicy READ useDefaultSizePolicy WRITE setUseDefaultSizePolicy NOTIFY useDefaultSizePolicyChanged FINAL REVISION(6, 8)) -+ Q_PROPERTY(QQuickLayout::SizePolicy useDefaultSizePolicy READ useDefaultSizePolicy WRITE setUseDefaultSizePolicy NOTIFY useDefaultSizePolicyChanged FINAL) - Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged FINAL) - Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged FINAL) - Q_PROPERTY(int rowSpan READ rowSpan WRITE setRowSpan NOTIFY rowSpanChanged FINAL) -@@ -347,7 +347,7 @@ Q_SIGNALS: - void maximumHeightChanged(); - void fillWidthChanged(); - void fillHeightChanged(); -- Q_REVISION(6, 8) void useDefaultSizePolicyChanged(); -+ void useDefaultSizePolicyChanged(); - void leftMarginChanged(); - void topMarginChanged(); - void rightMarginChanged(); diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0005-Use-new-signal-QAccessible-ScrollingPositionChanged-.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0005-Use-new-signal-QAccessible-ScrollingPositionChanged-.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0005-Use-new-signal-QAccessible-ScrollingPositionChanged-.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0005-Use-new-signal-QAccessible-ScrollingPositionChanged-.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,43 @@ +From 2c88eb2544ebb34082abe84a878ff2a22e308fbb Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Tue, 9 Sep 2025 08:10:14 +0200 +Subject: Use new signal QAccessible::ScrollingPositionChanged to inform a11y + layer + +Task-number: QTBUG-139833 +Change-Id: I95f920b6299d496a710234a123a5d544ef51f222 +--- + src/quick/items/qquickflickable.cpp | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git x/qtdeclarative/src/quick/items/qquickflickable.cpp y/qtdeclarative/src/quick/items/qquickflickable.cpp +index bc36805f2d46cff701f56ef1b0dc971cd5d7af3c..e129131117f8c87d6fb26c88bc00eddb96995719 100644 +--- x/qtdeclarative/src/quick/items/qquickflickable.cpp ++++ y/qtdeclarative/src/quick/items/qquickflickable.cpp +@@ -323,10 +323,24 @@ void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometr + if (vData.contentPositionChangedExternallyDuringDrag) + vData.pressPos += deltaMoved.y(); + } +- if (orient & Qt::Horizontal) ++ if (orient & Qt::Horizontal) { + emit q->contentXChanged(); +- if (orient & Qt::Vertical) ++#if QT_CONFIG(accessibility) ++ if (QAccessible::isActive()) { ++ QAccessibleEvent ev(q, QAccessible::ScrollingPositionChanged); ++ QAccessible::updateAccessibility(&ev); ++ } ++#endif ++ } ++ if (orient & Qt::Vertical) { + emit q->contentYChanged(); ++#if QT_CONFIG(accessibility) ++ if (QAccessible::isActive()) { ++ QAccessibleEvent ev(q, QAccessible::ScrollingPositionChanged); ++ QAccessible::updateAccessibility(&ev); ++ } ++#endif ++ } + } + } + diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0006-Consider-new-Switch-role-introduced-in-qtbase.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0006-Consider-new-Switch-role-introduced-in-qtbase.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0006-Consider-new-Switch-role-introduced-in-qtbase.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0006-Consider-new-Switch-role-introduced-in-qtbase.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,87 @@ +From 51dc80502b7fb64a4bf8bfe3e6d74311f1f31f84 Mon Sep 17 00:00:00 2001 +From: Lars Schmertmann +Date: Wed, 10 Sep 2025 11:55:58 +0200 +Subject: Consider new Switch role introduced in qtbase + +Task-number: QTBUG-139676 +Change-Id: Iea39ae7c18e71956e7a101a86425c84d876e8730 +Reviewed-by: Michael Weghorn +(cherry picked from commit a91e02ad06afe603d5296bb104b35657d4906a57) +--- + src/quick/accessible/qaccessiblequickitem.cpp | 9 +++++++-- + src/quick/items/qquickaccessibleattached.cpp | 9 +++++---- + 2 files changed, 12 insertions(+), 6 deletions(-) + +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +index 451a224af9004b8a48ca25254fbf4e08233f92e9..db2249ecd81a091d9f48cc5239b232099292777b 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +@@ -448,7 +448,10 @@ QAccessible::State QAccessibleQuickItem::state() const + state.invisible = true; + if (!viewRect_.intersects(itemRect)) + state.offscreen = true; +- if ((role() == QAccessible::CheckBox || role() == QAccessible::RadioButton) && object()->property("checked").toBool()) ++ if ((role() == QAccessible::CheckBox ++ || role() == QAccessible::RadioButton ++ || role() == QAccessible::Switch) ++ && object()->property("checked").toBool()) + state.checked = true; + if (item()->activeFocusOnTab() || isTextRole(role())) + state.focusable = true; +@@ -500,6 +503,7 @@ QStringList QAccessibleQuickItem::actionNames() const + break; + case QAccessible::RadioButton: + case QAccessible::CheckBox: ++ case QAccessible::Switch: + actions << QAccessibleActionInterface::toggleAction() + << QAccessibleActionInterface::pressAction(); + break; +@@ -548,7 +552,8 @@ void QAccessibleQuickItem::doAction(const QString &actionName) + // Value-based roles : (via the value interface: value, minimumValue, maximumValue), stepSize + switch (role()) { + case QAccessible::RadioButton: +- case QAccessible::CheckBox: { ++ case QAccessible::CheckBox: ++ case QAccessible::Switch: { + QVariant checked = object()->property("checked"); + if (checked.isValid()) { + if (actionName == QAccessibleActionInterface::toggleAction() || +diff --git x/qtdeclarative/src/quick/items/qquickaccessibleattached.cpp y/qtdeclarative/src/quick/items/qquickaccessibleattached.cpp +index 0bb8a35311344c74d52091254ea9d90e2286b706..466ff8972f80449c90113bb76eb60c8af74aad0a 100644 +--- x/qtdeclarative/src/quick/items/qquickaccessibleattached.cpp ++++ y/qtdeclarative/src/quick/items/qquickaccessibleattached.cpp +@@ -99,13 +99,13 @@ QT_BEGIN_NAMESPACE + that run on touch-only devices since screen readers often implement a virtual focus that + can be moved from item to item. + \row +- \li Button, CheckBox, RadioButton ++ \li Button, CheckBox, RadioButton, Switch + \li \l {Accessible::pressAction}{Accessible.pressAction} + \li A button should have a signal handler with the name \c onPressAction. + This signal may be emitted by an assistive tool such as a screen-reader. + The implementation needs to behave the same as a mouse click or tap on the button. + \row +- \li CheckBox, RadioButton ++ \li CheckBox, RadioButton, Switch + \li \l checkable, \l checked, \l {Accessible::toggleAction}{Accessible.toggleAction} + + \li The check state of the check box. Updated on Press, Check and Uncheck actions. +@@ -133,8 +133,8 @@ QT_BEGIN_NAMESPACE + \brief This property holds whether this item is focusable. + + By default, this property is \c false except for items where the role is one of +- \c CheckBox, \c RadioButton, \c Button, \c MenuItem, \c PageTab, \c EditableText, \c SpinBox, \c ComboBox, +- \c Terminal or \c ScrollBar. ++ \c CheckBox, \c RadioButton, \c Switch, \c Button, \c MenuItem, \c PageTab, ++ \c EditableText, \c SpinBox, \c ComboBox, \c Terminal or \c ScrollBar. + \sa focused + */ + /*! \qmlproperty bool QtQuick::Accessible::focused +@@ -383,6 +383,7 @@ void QQuickAccessibleAttached::setRole(QAccessible::Role role) + switch (role) { + case QAccessible::CheckBox: + case QAccessible::RadioButton: ++ case QAccessible::Switch: + if (!m_stateExplicitlySet.focusable) + m_state.focusable = true; + if (!m_stateExplicitlySet.checkable) diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0007-a11y-Consider-modal-QML-Popup-for-a11y-hierarchy.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0007-a11y-Consider-modal-QML-Popup-for-a11y-hierarchy.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0007-a11y-Consider-modal-QML-Popup-for-a11y-hierarchy.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0007-a11y-Consider-modal-QML-Popup-for-a11y-hierarchy.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,149 @@ +From f2986ebea8a75f9697867c895ab49b6b112f7b65 Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Mon, 15 Sep 2025 13:31:26 +0200 +Subject: a11y: Consider modal QML Popup for a11y hierarchy + +Introduce QAccessibleQuickPopupItem to allow access to the a11y properties +of the QML Popup. This uses the QQuickPopupItem as QQuickPopup does not +derive from QQuckItem and the popup item is the actual parent of the +popup's content. + +Via QAccessibleQuickPopupItem it is possible to set the `modal` flag +of QAccessible::State and use this to decide if the application is blocked +by a modal dialog. In this case only the top-most popup is exposed to the +a11y layer. + +As a fly-by this commit removes redudant curly braces in +qtquicktemplates2global.cpp. + +Fixes: QTBUG-140200 +Change-Id: Id4a89894bc0576410b8a184dd6f18bfb1c8b2b8f +--- + src/quick/accessible/qaccessiblequickitem.cpp | 8 ++++++ + src/quicktemplates/CMakeLists.txt | 1 + + .../accessible/qaccessiblequickpopupitem.cpp | 26 +++++++++++++++++++ + .../accessible/qaccessiblequickpopupitem_p.h | 22 ++++++++++++++++ + .../qtquicktemplates2global.cpp | 8 ++++-- + 5 files changed, 63 insertions(+), 2 deletions(-) + create mode 100644 src/quicktemplates/accessible/qaccessiblequickpopupitem.cpp + create mode 100644 src/quicktemplates/accessible/qaccessiblequickpopupitem_p.h + +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +index db2249ecd81a091d9f48cc5239b232099292777b..1bc956b1d50af51526b11e5e304b54f8c217c778 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +@@ -420,6 +420,14 @@ QList accessibleUnignoredChildren(QQuickItem *item, bool paintOrde + { + QList items; + unignoredChildren(item, &items, paintOrder); ++ ++ auto it = std::find_if(items.rbegin(), items.rend(), [](QQuickItem *item) { ++ const QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(item); ++ return iface && iface->state().modal && iface->role() == QAccessible::Dialog; ++ }); ++ if (it != items.rend()) ++ return { *it }; ++ + return items; + } + +diff --git x/qtdeclarative/src/quicktemplates/CMakeLists.txt y/qtdeclarative/src/quicktemplates/CMakeLists.txt +index 64be0cbe7cf8e8dbfa5f14b8b57fd7d3592567b1..644ad1afd7472201a42b0344d730e719fb0fa286 100644 +--- x/qtdeclarative/src/quicktemplates/CMakeLists.txt ++++ y/qtdeclarative/src/quicktemplates/CMakeLists.txt +@@ -142,6 +142,7 @@ qt_internal_extend_target(QuickTemplates2 CONDITION TARGET Qt::QmlModels + qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_accessibility + SOURCES + accessible/qaccessiblequickpage.cpp accessible/qaccessiblequickpage_p.h ++ accessible/qaccessiblequickpopupitem.cpp accessible/qaccessiblequickpopupitem_p.h + ) + + qt_internal_extend_target(QuickTemplates2 CONDITION QT_FEATURE_quicktemplates2_container +diff --git x/qtdeclarative/src/quicktemplates/accessible/qaccessiblequickpopupitem.cpp y/qtdeclarative/src/quicktemplates/accessible/qaccessiblequickpopupitem.cpp +new file mode 100644 +index 0000000000000000000000000000000000000000..818cfec605d45459bbbcd9fa5f32d3c6e38d7855 +--- /dev/null ++++ y/qtdeclarative/src/quicktemplates/accessible/qaccessiblequickpopupitem.cpp +@@ -0,0 +1,26 @@ ++// Copyright (C) 2025 The Qt Company Ltd. ++// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ++ ++#include "qaccessiblequickpopupitem_p.h" ++#include "qquickpopup_p.h" ++#include "qquickpopupitem_p_p.h" ++ ++QT_BEGIN_NAMESPACE ++ ++QAccessibleQuickPopupItem::QAccessibleQuickPopupItem(QQuickPopupItem *popupItem) ++ : QAccessibleQuickItem(popupItem) ++{ ++} ++ ++QAccessible::State QAccessibleQuickPopupItem::state() const ++{ ++ QAccessible::State state = QAccessibleQuickItem::state(); ++ ++ QQuickPopup *popup = static_cast(object()->parent()); ++ if (popup) ++ state.modal = popup->isModal(); ++ ++ return state; ++} ++ ++QT_END_NAMESPACE +diff --git x/qtdeclarative/src/quicktemplates/accessible/qaccessiblequickpopupitem_p.h y/qtdeclarative/src/quicktemplates/accessible/qaccessiblequickpopupitem_p.h +new file mode 100644 +index 0000000000000000000000000000000000000000..68580cf58de40ad93b860276632cf8113d54e153 +--- /dev/null ++++ y/qtdeclarative/src/quicktemplates/accessible/qaccessiblequickpopupitem_p.h +@@ -0,0 +1,22 @@ ++// Copyright (C) 2025 The Qt Company Ltd. ++// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ++ ++#ifndef QACCESSIBLEQUICKPOPUPITEM_H ++#define QACCESSIBLEQUICKPOPUPITEM_H ++ ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++class QQuickPopupItem; ++ ++class QAccessibleQuickPopupItem : public QAccessibleQuickItem ++{ ++public: ++ QAccessibleQuickPopupItem(QQuickPopupItem *popupItem); ++ QAccessible::State state() const override; ++}; ++ ++QT_END_NAMESPACE ++ ++#endif // QACCESSIBLEQUICKPOPUPITEM_H +diff --git x/qtdeclarative/src/quicktemplates/qtquicktemplates2global.cpp y/qtdeclarative/src/quicktemplates/qtquicktemplates2global.cpp +index e65eba230ee77e4c22da00fe1af4cade09542445..1db14d5038bd0f0ed34c6165676118fc32b49f7b 100644 +--- x/qtdeclarative/src/quicktemplates/qtquicktemplates2global.cpp ++++ y/qtdeclarative/src/quicktemplates/qtquicktemplates2global.cpp +@@ -7,7 +7,9 @@ + + #if QT_CONFIG(accessibility) + #include "qquickpage_p.h" ++#include "qquickpopupitem_p_p.h" + #include "accessible/qaccessiblequickpage_p.h" ++#include "accessible/qaccessiblequickpopupitem_p.h" + #endif + + QT_BEGIN_NAMESPACE +@@ -15,9 +17,11 @@ QT_BEGIN_NAMESPACE + #if QT_CONFIG(accessibility) + static QAccessibleInterface *qQuickAccessibleFactory(const QString &classname, QObject *object) + { +- if (classname == u"QQuickPage") { ++ if (classname == u"QQuickPage") + return new QAccessibleQuickPage(qobject_cast(object)); +- } ++ if (classname == u"QQuickPopupItem") ++ return new QAccessibleQuickPopupItem(qobject_cast(object)); ++ + return nullptr; + } + #endif diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0008-QQuickTextEdit-a11y-Allow-modifying-text-selection-v.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0008-QQuickTextEdit-a11y-Allow-modifying-text-selection-v.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0008-QQuickTextEdit-a11y-Allow-modifying-text-selection-v.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0008-QQuickTextEdit-a11y-Allow-modifying-text-selection-v.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,200 @@ +From 2a081464478f0091ad53ecba31e508d8bea1fb6f Mon Sep 17 00:00:00 2001 +From: Michael Weghorn +Date: Wed, 17 Sep 2025 12:39:04 +0200 +Subject: QQuickTextEdit a11y: Allow modifying text selection via a11y API + +Introduce new QAccessibleQuickTextEdit as a11y implementation +for QQuickTextEdit. + +Subclass the existing QAccessibleQuickItem and override the +methods to modify text selection. +(For retrieving the existing selection, the logic in +the base class is fine, but the TextEdit properties selectionStart +and selectionEnd are read-only, so cannot be set directly there.) + +Implement the QAccessibleTextInterface::addSelection logic +in the base class, so it can also be reused by the +QAccessibleQuickTextInput class that will be added +in an upcoming commit to implement similar logic +for QQuickTextInput and QQuickTextField. + +Extend the existing unit test to also test text selection. + +Task-number: QTBUG-139943 +Pick-to: 6.10 6.9 6.8 +Change-Id: Ibd6aa3e1bc9a24352c9bef4fcf4a452252d6689c +--- + src/quick/CMakeLists.txt | 1 + + src/quick/accessible/qaccessiblequickitem.cpp | 4 +- + .../accessible/qaccessiblequicktextedit.cpp | 31 ++++++++++++++ + .../accessible/qaccessiblequicktextedit_p.h | 42 +++++++++++++++++++ + .../accessible/qquickaccessiblefactory.cpp | 8 +++- + .../qquickaccessible/tst_qquickaccessible.cpp | 11 +++++ + 6 files changed, 93 insertions(+), 4 deletions(-) + create mode 100644 src/quick/accessible/qaccessiblequicktextedit.cpp + create mode 100644 src/quick/accessible/qaccessiblequicktextedit_p.h + +diff --git x/qtdeclarative/src/quick/CMakeLists.txt y/qtdeclarative/src/quick/CMakeLists.txt +index d3d9087a84929b2223fbd300ebb5c14d87679be7..087087982c24ec1eeb08353aabbfe889175694e5 100644 +--- x/qtdeclarative/src/quick/CMakeLists.txt ++++ y/qtdeclarative/src/quick/CMakeLists.txt +@@ -569,6 +569,7 @@ qt_internal_extend_target(Quick CONDITION QT_FEATURE_quick_designer + qt_internal_extend_target(Quick CONDITION QT_FEATURE_accessibility + SOURCES + accessible/qaccessiblequickitem.cpp accessible/qaccessiblequickitem_p.h ++ accessible/qaccessiblequicktextedit.cpp accessible/qaccessiblequicktextedit_p.h + accessible/qaccessiblequickview.cpp accessible/qaccessiblequickview_p.h + accessible/qquickaccessiblefactory.cpp accessible/qquickaccessiblefactory_p.h + LIBRARIES +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +index 1bc956b1d50af51526b11e5e304b54f8c217c778..484955c395ba5dd432251c55cd8f4a5882e8aff5 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +@@ -997,9 +997,9 @@ int QAccessibleQuickItem::selectionCount() const + return 0; + } + +-void QAccessibleQuickItem::addSelection(int /* startOffset */, int /* endOffset */) ++void QAccessibleQuickItem::addSelection(int startOffset, int endOffset) + { +- ++ setSelection(0, startOffset, endOffset); + } + void QAccessibleQuickItem::removeSelection(int /* selectionIndex */) + { +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequicktextedit.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequicktextedit.cpp +new file mode 100644 +index 0000000000000000000000000000000000000000..780d69cf9b53f4527725e9eddc494b163b576c70 +--- /dev/null ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequicktextedit.cpp +@@ -0,0 +1,31 @@ ++// Copyright (C) 2025 The Qt Company Ltd. ++// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ++ ++#include "qaccessiblequicktextedit_p.h" ++ ++QT_BEGIN_NAMESPACE ++ ++#if QT_CONFIG(accessibility) ++ ++QAccessibleQuickTextEdit::QAccessibleQuickTextEdit(QQuickTextEdit *textEdit) ++ : QAccessibleQuickItem(textEdit) ++{ ++} ++ ++void QAccessibleQuickTextEdit::removeSelection(int selectionIndex) ++{ ++ if (selectionCount() == 1 && selectionIndex == 0) { ++ const int cursorPos = textEdit()->cursorPosition(); ++ textEdit()->select(cursorPos, cursorPos); ++ } ++} ++ ++void QAccessibleQuickTextEdit::setSelection(int selectionIndex, int startOffset, int endOffset) ++{ ++ if (selectionIndex == 0) ++ textEdit()->select(startOffset, endOffset); ++} ++ ++#endif // accessibility ++ ++QT_END_NAMESPACE +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequicktextedit_p.h y/qtdeclarative/src/quick/accessible/qaccessiblequicktextedit_p.h +new file mode 100644 +index 0000000000000000000000000000000000000000..c4a7029b904673e92bca178635765158ca13b0d9 +--- /dev/null ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequicktextedit_p.h +@@ -0,0 +1,42 @@ ++// Copyright (C) 2025 The Qt Company Ltd. ++// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ++ ++#ifndef QACCESSIBLEQUICKTEXTEDIT_H ++#define QACCESSIBLEQUICKTEXTEDIT_H ++ ++// ++// W A R N I N G ++// ------------- ++// ++// This file is not part of the Qt API. It exists purely as an ++// implementation detail. This header file may change from version to ++// version without notice, or even be removed. ++// ++// We mean it. ++// ++ ++#include "qaccessiblequickitem_p.h" ++ ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++#if QT_CONFIG(accessibility) ++ ++class Q_QUICK_EXPORT QAccessibleQuickTextEdit : public QAccessibleQuickItem ++{ ++public: ++ QAccessibleQuickTextEdit(QQuickTextEdit *textEdit); ++ ++ void removeSelection(int selectionIndex) override; ++ void setSelection(int selectionIndex, int startOffset, int endOffset) override; ++ ++private: ++ QQuickTextEdit *textEdit() const { return static_cast(item()); } ++}; ++ ++#endif // accessibility ++ ++QT_END_NAMESPACE ++ ++#endif // QACCESSIBLEQUICKTEXTEDIT_H +diff --git x/qtdeclarative/src/quick/accessible/qquickaccessiblefactory.cpp y/qtdeclarative/src/quick/accessible/qquickaccessiblefactory.cpp +index 00542a2b8302edfc4179d30530ca4c57fd64146f..f8bce6a663040b850aef08a52202ae07b7ba3f7f 100644 +--- x/qtdeclarative/src/quick/accessible/qquickaccessiblefactory.cpp ++++ y/qtdeclarative/src/quick/accessible/qquickaccessiblefactory.cpp +@@ -5,16 +5,20 @@ + + #include "qaccessiblequickview_p.h" + #include "qaccessiblequickitem_p.h" ++#include "qaccessiblequicktextedit_p.h" + #include ++#include + + QT_BEGIN_NAMESPACE + #if QT_CONFIG(accessibility) + + QAccessibleInterface *qQuickAccessibleFactory(const QString &classname, QObject *object) + { +- if (classname == QLatin1String("QQuickWindow")) { ++ if (classname == QLatin1String("QQuickWindow")) + return new QAccessibleQuickWindow(qobject_cast(object)); +- } else if (classname == QLatin1String("QQuickItem")) { ++ if (classname == QLatin1String("QQuickTextEdit")) ++ return new QAccessibleQuickTextEdit(qobject_cast(object)); ++ if (classname == QLatin1String("QQuickItem")) { + QQuickItem *item = qobject_cast(object); + Q_ASSERT(item); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); +diff --git x/qtdeclarative/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp y/qtdeclarative/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +index 375a7e502f7ef5e959a6ea273e622d6981e42193..1f7e27e0404311d70120b03ea8eddbfee24beb15 100644 +--- x/qtdeclarative/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp ++++ y/qtdeclarative/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +@@ -522,8 +522,19 @@ void tst_QQuickAccessible::basicPropertiesTest() + QVERIFY(!textEdit->state().readOnly); + QVERIFY(textEdit->state().focusable); + QCOMPARE(textEdit->text(QAccessible::Value), "A multi-line text edit\nTesting Accessibility."); ++ + auto textEditTextInterface = textEdit->textInterface(); + QVERIFY(textEditTextInterface); ++ QCOMPARE(textEditTextInterface->selectionCount(), 0); ++ textEditTextInterface->setSelection(0, 1, 4); ++ QCOMPARE(textEditTextInterface->selectionCount(), 1); ++ int selectionStart = 0, selectionEnd = 0; ++ textEditTextInterface->selection(0, &selectionStart, &selectionEnd); ++ QCOMPARE(selectionStart, 1); ++ QCOMPARE(selectionEnd, 4); ++ textEditTextInterface->removeSelection(0); ++ QCOMPARE(textEditTextInterface->selectionCount(), 0); ++ + auto textEditEditableTextInterface = textEdit->editableTextInterface(); + QEXPECT_FAIL("", "EditableTextInterface is not implemented", Continue); + QVERIFY(textEditEditableTextInterface); diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0009-QQuickTextInput-a11y-Allow-modifying-text-selection-.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0009-QQuickTextInput-a11y-Allow-modifying-text-selection-.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0009-QQuickTextInput-a11y-Allow-modifying-text-selection-.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0009-QQuickTextInput-a11y-Allow-modifying-text-selection-.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,170 @@ +From cb5a3711c6bc5928865916175bfb388829348822 Mon Sep 17 00:00:00 2001 +From: Michael Weghorn +Date: Mon, 22 Sep 2025 14:46:12 +0200 +Subject: QQuickTextInput a11y: Allow modifying text selection via a11y API + +Similar to the previous commit that introduced +QAccessibleQuickTextEdit, introduce QAccessibleQuickTextInput +and implement the logic to modify selection. + +This allows changing the selection for QQuickTextInput +and its QQuickTextField subclass via the platform +accessibility API, e.g. the AT-SPI Text interface's +SetSelection method. + +Extend the existing TextInput autotest accordingly. + +Task-number: QTBUG-139943 +Task-number: QTBUG-140441 +Change-Id: Ie05c6b0cf3431aabb4b337dbfa07c40c56660e2b +--- + src/quick/CMakeLists.txt | 1 + + .../accessible/qaccessiblequicktextinput.cpp | 31 ++++++++++++++ + .../accessible/qaccessiblequicktextinput_p.h | 42 +++++++++++++++++++ + .../accessible/qquickaccessiblefactory.cpp | 4 ++ + .../qquickaccessible/tst_qquickaccessible.cpp | 9 ++++ + 5 files changed, 87 insertions(+) + create mode 100644 src/quick/accessible/qaccessiblequicktextinput.cpp + create mode 100644 src/quick/accessible/qaccessiblequicktextinput_p.h + +diff --git x/qtdeclarative/src/quick/CMakeLists.txt y/qtdeclarative/src/quick/CMakeLists.txt +index 087087982c24ec1eeb08353aabbfe889175694e5..8103f939274a728234441b8d340e257f419f020b 100644 +--- x/qtdeclarative/src/quick/CMakeLists.txt ++++ y/qtdeclarative/src/quick/CMakeLists.txt +@@ -570,6 +570,7 @@ qt_internal_extend_target(Quick CONDITION QT_FEATURE_accessibility + SOURCES + accessible/qaccessiblequickitem.cpp accessible/qaccessiblequickitem_p.h + accessible/qaccessiblequicktextedit.cpp accessible/qaccessiblequicktextedit_p.h ++ accessible/qaccessiblequicktextinput.cpp accessible/qaccessiblequicktextinput_p.h + accessible/qaccessiblequickview.cpp accessible/qaccessiblequickview_p.h + accessible/qquickaccessiblefactory.cpp accessible/qquickaccessiblefactory_p.h + LIBRARIES +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput.cpp +new file mode 100644 +index 0000000000000000000000000000000000000000..23132e0cedc2e11d8f658bc6b61de0a71332d5b7 +--- /dev/null ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput.cpp +@@ -0,0 +1,31 @@ ++// Copyright (C) 2025 The Qt Company Ltd. ++// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ++ ++#include "qaccessiblequicktextinput_p.h" ++ ++QT_BEGIN_NAMESPACE ++ ++#if QT_CONFIG(accessibility) ++ ++QAccessibleQuickTextInput::QAccessibleQuickTextInput(QQuickTextInput *textEdit) ++ : QAccessibleQuickItem(textEdit) ++{ ++} ++ ++void QAccessibleQuickTextInput::removeSelection(int selectionIndex) ++{ ++ if (selectionCount() == 1 && selectionIndex == 0) { ++ const int cursorPos = textInput()->cursorPosition(); ++ textInput()->select(cursorPos, cursorPos); ++ } ++} ++ ++void QAccessibleQuickTextInput::setSelection(int selectionIndex, int startOffset, int endOffset) ++{ ++ if (selectionIndex == 0) ++ textInput()->select(startOffset, endOffset); ++} ++ ++#endif // accessibility ++ ++QT_END_NAMESPACE +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput_p.h y/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput_p.h +new file mode 100644 +index 0000000000000000000000000000000000000000..1ba46f0f601d087b839e320b480f2fd5379b9ec8 +--- /dev/null ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput_p.h +@@ -0,0 +1,42 @@ ++// Copyright (C) 2025 The Qt Company Ltd. ++// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ++ ++#ifndef QACCESSIBLEQUICKTEXTINPUT_H ++#define QACCESSIBLEQUICKTEXTINPUT_H ++ ++// ++// W A R N I N G ++// ------------- ++// ++// This file is not part of the Qt API. It exists purely as an ++// implementation detail. This header file may change from version to ++// version without notice, or even be removed. ++// ++// We mean it. ++// ++ ++#include "qaccessiblequickitem_p.h" ++ ++#include ++ ++QT_BEGIN_NAMESPACE ++ ++#if QT_CONFIG(accessibility) ++ ++class Q_QUICK_EXPORT QAccessibleQuickTextInput : public QAccessibleQuickItem ++{ ++public: ++ QAccessibleQuickTextInput(QQuickTextInput *textEdit); ++ ++ void removeSelection(int selectionIndex) override; ++ void setSelection(int selectionIndex, int startOffset, int endOffset) override; ++ ++private: ++ QQuickTextInput *textInput() const { return static_cast(item()); } ++}; ++ ++#endif // accessibility ++ ++QT_END_NAMESPACE ++ ++#endif // QACCESSIBLEQUICKTEXTINPUT_H +diff --git x/qtdeclarative/src/quick/accessible/qquickaccessiblefactory.cpp y/qtdeclarative/src/quick/accessible/qquickaccessiblefactory.cpp +index f8bce6a663040b850aef08a52202ae07b7ba3f7f..88614307557c73399bd3ae885f211e1a70fa0cf1 100644 +--- x/qtdeclarative/src/quick/accessible/qquickaccessiblefactory.cpp ++++ y/qtdeclarative/src/quick/accessible/qquickaccessiblefactory.cpp +@@ -6,8 +6,10 @@ + #include "qaccessiblequickview_p.h" + #include "qaccessiblequickitem_p.h" + #include "qaccessiblequicktextedit_p.h" ++#include "qaccessiblequicktextinput_p.h" + #include + #include ++#include + + QT_BEGIN_NAMESPACE + #if QT_CONFIG(accessibility) +@@ -18,6 +20,8 @@ QAccessibleInterface *qQuickAccessibleFactory(const QString &classname, QObject + return new QAccessibleQuickWindow(qobject_cast(object)); + if (classname == QLatin1String("QQuickTextEdit")) + return new QAccessibleQuickTextEdit(qobject_cast(object)); ++ if (classname == QLatin1String("QQuickTextInput")) ++ return new QAccessibleQuickTextInput(qobject_cast(object)); + if (classname == QLatin1String("QQuickItem")) { + QQuickItem *item = qobject_cast(object); + Q_ASSERT(item); +diff --git x/qtdeclarative/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp y/qtdeclarative/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +index 1f7e27e0404311d70120b03ea8eddbfee24beb15..cc9aece71b9d11d3145e90473f45091a81430a21 100644 +--- x/qtdeclarative/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp ++++ y/qtdeclarative/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +@@ -512,6 +512,15 @@ void tst_QQuickAccessible::basicPropertiesTest() + auto newText = QString("a new text"); + textInput->setText(QAccessible::Value, newText); + QCOMPARE(textInput->text(QAccessible::Value), newText); ++ QCOMPARE(textInterface->selectionCount(), 0); ++ textInterface->setSelection(0, 1, 4); ++ QCOMPARE(textInterface->selectionCount(), 1); ++ int selectionStartOffset = 0, selectionEndOffset = 0; ++ textInterface->selection(0, &selectionStartOffset, &selectionEndOffset); ++ QCOMPARE(selectionStartOffset, 1); ++ QCOMPARE(selectionEndOffset, 4); ++ textInterface->removeSelection(0); ++ QCOMPARE(textInterface->selectionCount(), 0); + + // TextEdit + QAccessibleInterface *textEdit = item->child(3); diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0010-A11y-Prepend-new-items-in-ItemView-based-on-modelInd.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0010-A11y-Prepend-new-items-in-ItemView-based-on-modelInd.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0010-A11y-Prepend-new-items-in-ItemView-based-on-modelInd.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0010-A11y-Prepend-new-items-in-ItemView-based-on-modelInd.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,120 @@ +From b9688d83aa0c30c486761bca029719db029dd703 Mon Sep 17 00:00:00 2001 +From: Jens Trillmann +Date: Thu, 18 Sep 2025 09:42:52 +0200 +Subject: A11y: Prepend new items in ItemView based on modelIndex + +When traversing list elements with a screen reader backwards the elements become +unordered as new elements are only appended to the list. By prepending new items when necessary the elements are correctly ordered. + +Task-number: QTBUG-140463 +Pick-to: 6.10 +Change-Id: I76e6bd8247a4d37d4508fb0f79cd39e7594858ca +--- + src/quick/items/qquickitem.cpp | 13 ++++++++----- + src/quick/items/qquickitem.h | 2 +- + src/quick/items/qquickitem_p.h | 2 +- + src/quick/items/qquickitemview.cpp | 10 ++++++++-- + 4 files changed, 18 insertions(+), 9 deletions(-) + +diff --git x/qtdeclarative/src/quick/items/qquickitem.cpp y/qtdeclarative/src/quick/items/qquickitem.cpp +index 9e1e4a9a761ea6596a9395f9e870099ec8393143..d66a06a68b5fec81dcf237da8719d18b385e6554 100644 +--- x/qtdeclarative/src/quick/items/qquickitem.cpp ++++ y/qtdeclarative/src/quick/items/qquickitem.cpp +@@ -2731,7 +2731,7 @@ QQuickItem *QQuickItem::parentItem() const + return d->parentItem; + } + +-void QQuickItem::setParentItem(QQuickItem *parentItem) ++void QQuickItem::setParentItem(QQuickItem *parentItem, bool prepend) + { + Q_D(QQuickItem); + if (parentItem == d->parentItem) +@@ -2803,7 +2803,7 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) + auto oldParentItem = d->parentItem; + d->parentItem = parentItem; + if (d->parentItem) { +- QQuickItemPrivate::get(d->parentItem)->addChild(this); ++ QQuickItemPrivate::get(d->parentItem)->addChild(this, prepend); + alreadyAddedChild = true; + } + if (d->window) { +@@ -2823,7 +2823,7 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) + d->dirty(QQuickItemPrivate::ParentChanged); + + if (d->parentItem && !alreadyAddedChild) +- QQuickItemPrivate::get(d->parentItem)->addChild(this); ++ QQuickItemPrivate::get(d->parentItem)->addChild(this, prepend); + else if (d->window && !alreadyAddedChild) + QQuickWindowPrivate::get(d->window)->parentlessItems.insert(this); + +@@ -3013,13 +3013,16 @@ QList QQuickItemPrivate::paintOrderChildItems() const + return childItems; + } + +-void QQuickItemPrivate::addChild(QQuickItem *child) ++void QQuickItemPrivate::addChild(QQuickItem *child, bool prepend) + { + Q_Q(QQuickItem); + + Q_ASSERT(!childItems.contains(child)); + +- childItems.append(child); ++ if (prepend) ++ childItems.prepend(child); ++ else ++ childItems.append(child); + + QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); + +diff --git x/qtdeclarative/src/quick/items/qquickitem.h y/qtdeclarative/src/quick/items/qquickitem.h +index 427eed42e9a6bc7b39ffa7623d4d3048a73581f0..a842967fde0393351ab760d2648b845269a4942b 100644 +--- x/qtdeclarative/src/quick/items/qquickitem.h ++++ y/qtdeclarative/src/quick/items/qquickitem.h +@@ -182,7 +182,7 @@ public: + + QQuickWindow *window() const; + QQuickItem *parentItem() const; +- void setParentItem(QQuickItem *parent); ++ void setParentItem(QQuickItem *parent, bool prepend = false); + void stackBefore(const QQuickItem *); + void stackAfter(const QQuickItem *); + +diff --git x/qtdeclarative/src/quick/items/qquickitem_p.h y/qtdeclarative/src/quick/items/qquickitem_p.h +index 1e98dd888692ee867f45f46ab8b143a2b79f9992..7c01f5ed08dcd92869a5cd7e7c2e833bfef6a14a 100644 +--- x/qtdeclarative/src/quick/items/qquickitem_p.h ++++ y/qtdeclarative/src/quick/items/qquickitem_p.h +@@ -608,7 +608,7 @@ public: + QList childItems; + mutable QList *sortedChildItems; + QList paintOrderChildItems() const; +- void addChild(QQuickItem *); ++ void addChild(QQuickItem *, bool prepend = false); + void removeChild(QQuickItem *); + void siblingOrderChanged(); + +diff --git x/qtdeclarative/src/quick/items/qquickitemview.cpp y/qtdeclarative/src/quick/items/qquickitemview.cpp +index e1a51d0413253efe1c0813084dc2aff6de2f2734..bac17546bca6f495580ae3123cbe6045b3956f28 100644 +--- x/qtdeclarative/src/quick/items/qquickitemview.cpp ++++ y/qtdeclarative/src/quick/items/qquickitemview.cpp +@@ -2497,13 +2497,19 @@ void QQuickItemView::createdItem(int index, QObject* object) + } + } + +-void QQuickItemView::initItem(int, QObject *object) ++void QQuickItemView::initItem(int index, QObject *object) + { ++ Q_D(QQuickItemView); + QQuickItem* item = qmlobject_cast(object); + if (item) { + if (qFuzzyIsNull(item->z())) + item->setZ(1); +- item->setParentItem(contentItem()); ++ FxViewItem* firstItem = d->firstItemInView(); ++ bool prepend = false; ++ if (firstItem) ++ prepend = index < firstItem->index; ++ ++ item->setParentItem(contentItem(), prepend); + QQuickItemPrivate::get(item)->setCulled(true); + } + } diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0011-Remove-special-treatment-for-embedded-hyperlinks.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0011-Remove-special-treatment-for-embedded-hyperlinks.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0011-Remove-special-treatment-for-embedded-hyperlinks.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0011-Remove-special-treatment-for-embedded-hyperlinks.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,101 @@ +From 9c40ba00309cce7dc41065eb1c7d8a572a5c4bee Mon Sep 17 00:00:00 2001 +From: Jan Moeller +Date: Tue, 14 Oct 2025 07:29:42 +0200 +Subject: Remove special treatment for embedded hyperlinks + +We don't want the links as an extra element in the a11y hierarchy to allow us +to tell the screen reader that the link will open in a browser and with which +URL. The embedded element is not transparent to us and we can't set any +properties on it. So we remove it altogether. + +Change-Id: I19c4b5e17250cf9933ef635f1370f394058676f3 +--- + src/quick/accessible/qaccessiblequickitem.cpp | 52 +------------------ + 1 file changed, 1 insertion(+), 51 deletions(-) + +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +index 484955c395ba5dd432251c55cd8f4a5882e8aff5..b2b8ddd84666bf1f1aceaad23c8d0dcc79c0cf7b 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +@@ -229,16 +229,7 @@ QWindow *QAccessibleQuickItem::window() const + return window; + } + +-int QAccessibleQuickItem::childCount() const +-{ +- // see comment in QAccessibleQuickItem::child() as to why we do this +- int cc = 0; +- if (QQuickText *textItem = qobject_cast(item())) { +- cc = QQuickTextPrivate::get(textItem)->getLinks().size(); +- } +- cc += childItems().size(); +- return cc; +-} ++int QAccessibleQuickItem::childCount() const { return childItems().size(); } + + QRect QAccessibleQuickItem::rect() const + { +@@ -271,17 +262,6 @@ QAccessibleInterface *QAccessibleQuickItem::childAt(int x, int y) const + return nullptr; + } + +- // special case for text interfaces +- if (QQuickText *textItem = qobject_cast(item())) { +- const auto hyperLinkChildCount = QQuickTextPrivate::get(textItem)->getLinks().size(); +- for (auto i = 0; i < hyperLinkChildCount; i++) { +- QAccessibleInterface *iface = child(i); +- if (iface->rect().contains(x,y)) { +- return iface; +- } +- } +- } +- + // general item hit test + const QList kids = accessibleUnignoredChildren(item(), true); + for (int i = kids.size() - 1; i >= 0; --i) { +@@ -358,22 +338,6 @@ QAccessibleInterface *QAccessibleQuickItem::child(int index) const + if (index < 0) + return nullptr; + +- +- if (QQuickText *textItem = qobject_cast(item())) { +- const int hyperLinkChildCount = QQuickTextPrivate::get(textItem)->getLinks().size(); +- if (index < hyperLinkChildCount) { +- auto it = m_childToId.constFind(index); +- if (it != m_childToId.constEnd()) +- return QAccessible::accessibleInterface(it.value()); +- +- QAccessibleHyperlink *iface = new QAccessibleHyperlink(item(), index); +- QAccessible::Id id = QAccessible::registerAccessibleInterface(iface); +- m_childToId.insert(index, id); +- return iface; +- } +- index -= hyperLinkChildCount; +- } +- + QList children = childItems(); + if (index < children.size()) { + QQuickItem *child = children.at(index); +@@ -384,22 +348,8 @@ QAccessibleInterface *QAccessibleQuickItem::child(int index) const + + int QAccessibleQuickItem::indexOfChild(const QAccessibleInterface *iface) const + { +- int hyperLinkChildCount = 0; +- if (QQuickText *textItem = qobject_cast(item())) { +- hyperLinkChildCount = QQuickTextPrivate::get(textItem)->getLinks().size(); +- if (QAccessibleHyperlinkInterface *hyperLinkIface = const_cast(iface)->hyperlinkInterface()) { +- // ### assumes that there is only one subclass implementing QAccessibleHyperlinkInterface +- // Alternatively, we could simply iterate with child() and do a linear search for it +- QAccessibleHyperlink *hyperLink = static_cast(hyperLinkIface); +- if (hyperLink->textItem() == static_cast(item())) { +- return hyperLink->linkIndex; +- } +- } +- } + QList kids = childItems(); + int idx = kids.indexOf(static_cast(iface->object())); +- if (idx >= 0) +- idx += hyperLinkChildCount; + return idx; + } + diff -Nru ausweisapp2-2.3.1/libs/patches/qtdeclarative-0012-A11y-Implement-QAccessibleEditableTextInterface-for-.patch ausweisapp2-2.4.0/libs/patches/qtdeclarative-0012-A11y-Implement-QAccessibleEditableTextInterface-for-.patch --- ausweisapp2-2.3.1/libs/patches/qtdeclarative-0012-A11y-Implement-QAccessibleEditableTextInterface-for-.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtdeclarative-0012-A11y-Implement-QAccessibleEditableTextInterface-for-.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,145 @@ +From 327f8273363c3a82dd2fa498cbcd81e01b6e22f4 Mon Sep 17 00:00:00 2001 +From: Jens Trillmann +Date: Thu, 16 Oct 2025 18:06:53 +0200 +Subject: A11y: Implement QAccessibleEditableTextInterface for TextInput + +Change-Id: I211278ec3ec32554abcd163933bdf2156183d27c +--- + src/quick/accessible/qaccessiblequickitem.cpp | 23 +++++++++++++++ + src/quick/accessible/qaccessiblequickitem_p.h | 6 ++++ + .../accessible/qaccessiblequicktextedit.cpp | 1 + + .../accessible/qaccessiblequicktextinput.cpp | 28 +++++++++++++++++++ + .../accessible/qaccessiblequicktextinput_p.h | 4 +++ + 5 files changed, 62 insertions(+) + +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +index b2b8ddd84666bf1f1aceaad23c8d0dcc79c0cf7b..fb7c3237d2d8db37a553e67dd68d06e115b80141 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequickitem.cpp +@@ -657,6 +657,12 @@ void *QAccessibleQuickItem::interface_cast(QAccessible::InterfaceType t) + r == QAccessible::ProgressBar)) + return static_cast(this); + ++ if (t == QAccessible::EditableTextInterface) { ++ if (r == QAccessible::EditableText) { ++ return static_cast(this); ++ } ++ } ++ + if (t == QAccessible::TextInterface) { + if (r == QAccessible::EditableText || + r == QAccessible::StaticText) +@@ -960,6 +966,23 @@ void QAccessibleQuickItem::setSelection(int /* selectionIndex */, int /* startOf + + } + ++void QAccessibleQuickItem::deleteText(int /* startOffset */, int /* endOffset */) ++{ ++ ++} ++ ++ ++void QAccessibleQuickItem::insertText(int /* offset */, const QString &/* text */) ++{ ++ ++} ++ ++ ++void QAccessibleQuickItem::replaceText(int /* startOffset */, int /* endOffset */, const QString &/* text */) ++{ ++ ++} ++ + + #endif // accessibility + +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequickitem_p.h y/qtdeclarative/src/quick/accessible/qaccessiblequickitem_p.h +index 33b4e6476c8c093767f8b4b07621988b3a1f59e3..ea9d1c2ac1e7097ba0e1f52cc63481217e92fea8 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequickitem_p.h ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequickitem_p.h +@@ -30,6 +30,7 @@ class Q_QUICK_EXPORT QAccessibleQuickItem : public QAccessibleObject, + public QAccessibleActionInterface, + public QAccessibleValueInterface, + public QAccessibleTextInterface, ++ public QAccessibleEditableTextInterface, + public QAccessibleScrollInterface + { + public: +@@ -84,6 +85,11 @@ public: + void removeSelection(int selectionIndex) override; + void setSelection(int selectionIndex, int startOffset, int endOffset) override; + ++ // Editable Text Interface ++ void deleteText(int startOffset, int endOffset) override; ++ void insertText(int offset, const QString &text) override; ++ void replaceText(int startOffset, int endOffset, const QString &text) override; ++ + // cursor + int cursorPosition() const override; + void setCursorPosition(int position) override; +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequicktextedit.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequicktextedit.cpp +index 780d69cf9b53f4527725e9eddc494b163b576c70..09dd85597092834d5b3aeda062f7503b86e34baf 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequicktextedit.cpp ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequicktextedit.cpp +@@ -26,6 +26,7 @@ void QAccessibleQuickTextEdit::setSelection(int selectionIndex, int startOffset, + textEdit()->select(startOffset, endOffset); + } + ++ + #endif // accessibility + + QT_END_NAMESPACE +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput.cpp y/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput.cpp +index 23132e0cedc2e11d8f658bc6b61de0a71332d5b7..32ff8a9675346cb944050090b595c6121736afcf 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput.cpp ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput.cpp +@@ -26,6 +26,34 @@ void QAccessibleQuickTextInput::setSelection(int selectionIndex, int startOffset + textInput()->select(startOffset, endOffset); + } + ++void QAccessibleQuickTextInput::deleteText(int startOffset, int endOffset) ++{ ++ if (startOffset >= 0 && startOffset <= textInput()->length() ++ && endOffset >= 0 && endOffset <= textInput()->length()) { ++ textInput()->deselect(); ++ textInput()->remove(startOffset, endOffset); ++ } ++} ++ ++void QAccessibleQuickTextInput::insertText(int offset, const QString &text) ++{ ++ if (offset >= 0 && offset <= textInput()->length()) { ++ textInput()->deselect(); ++ textInput()->insert(offset, text); ++ } ++} ++ ++void QAccessibleQuickTextInput::replaceText(int startOffset, int endOffset, const QString &text) ++{ ++ if (startOffset >= 0 && startOffset <= textInput()->length() ++ && endOffset >= 0 && endOffset <= textInput()->length() ++ && !text.isEmpty()) { ++ textInput()->deselect(); ++ textInput()->remove(startOffset, endOffset); ++ textInput()->insert(startOffset, text); ++ } ++} ++ + #endif // accessibility + + QT_END_NAMESPACE +diff --git x/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput_p.h y/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput_p.h +index 1ba46f0f601d087b839e320b480f2fd5379b9ec8..b4571aa7cf47d450e65256b91ee5d2ba2eb4ef9f 100644 +--- x/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput_p.h ++++ y/qtdeclarative/src/quick/accessible/qaccessiblequicktextinput_p.h +@@ -31,6 +31,10 @@ public: + void removeSelection(int selectionIndex) override; + void setSelection(int selectionIndex, int startOffset, int endOffset) override; + ++ void deleteText(int startOffset, int endOffset) override; ++ void insertText(int offset, const QString &text) override; ++ void replaceText(int startOffset, int endOffset, const QString &text) override; ++ + private: + QQuickTextInput *textInput() const { return static_cast(item()); } + }; diff -Nru ausweisapp2-2.3.1/libs/patches/qtscxml-0001-Fix-features-and-add-some-new-features.patch ausweisapp2-2.4.0/libs/patches/qtscxml-0001-Fix-features-and-add-some-new-features.patch --- ausweisapp2-2.3.1/libs/patches/qtscxml-0001-Fix-features-and-add-some-new-features.patch 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtscxml-0001-Fix-features-and-add-some-new-features.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,270 +0,0 @@ -From 45df93928d3bd5603667bc215e8620ac072d0a79 Mon Sep 17 00:00:00 2001 -From: Lars Schmertmann -Date: Mon, 6 Jan 2025 18:19:33 +0100 -Subject: Fix features and add some new features - -* qtscxml contains two independent implementations. With Qt 6 the - statemachine was moved into qtscxml to avoid an additional module - that provides similar functions. During this process the features - were broken. -* With this change the features are fixed and additional features are - added to enable fine-grained configuration for an efficient build. -* The schema was taken from qttools. - -Fixes: QTBUG-132565 -Pick-to: 6.9 -Change-Id: I638a8d08f94425dba3d80a68af389654458ae8dc -Reviewed-by: Alexandru Croitor -Reviewed-by: Ulf Hermann -(cherry picked from commit d59b74c12cbd76f129db5b12246749206bcd3b29) ---- - configure.cmake | 41 ++++++++++++++++++++++++++ - src/CMakeLists.txt | 20 +++++++++---- - src/global/CMakeLists.txt | 15 ++++++++++ - src/scxml/CMakeLists.txt | 1 + - src/scxml/configure.cmake | 28 ------------------ - src/scxml/qscxmlglobals.h | 2 +- - src/statemachine/CMakeLists.txt | 1 + - src/statemachine/configure.cmake | 29 ------------------ - src/statemachine/qstatemachineglobal.h | 2 +- - tools/CMakeLists.txt | 2 +- - 10 files changed, 76 insertions(+), 65 deletions(-) - create mode 100644 src/global/CMakeLists.txt - delete mode 100644 src/scxml/configure.cmake - delete mode 100644 src/statemachine/configure.cmake - -diff --git x/qtscxml/configure.cmake y/qtscxml/configure.cmake -index 68f54ce726cbef1df26fee83dbfb1478e8995a9e..337a5d90a6c655e2897e14bb44e617aeb239822a 100644 ---- x/qtscxml/configure.cmake -+++ y/qtscxml/configure.cmake -@@ -17,6 +17,47 @@ - - #### Features - -+qt_feature("scxml" PUBLIC -+ LABEL "Qt SCXML" -+ PURPOSE "Allows embedding of state machines created from State Chart XML (SCXML) files." -+) -+qt_feature("scxml-qml" PUBLIC -+ LABEL "SCXML QML Types" -+ PURPOSE "Provides QML Types for Qt SCXML." -+ CONDITION QT_FEATURE_scxml AND TARGET Qt::Qml -+) -+qt_feature("scxml-ecmascriptdatamodel" PUBLIC -+ LABEL "ECMAScript data model for QtScxml" -+ PURPOSE "Enables the usage of ecmascript data models in SCXML state machines." -+ CONDITION QT_FEATURE_scxml AND TARGET Qt::Qml -+) -+ -+qt_feature("statemachine" PUBLIC -+ LABEL "Qt State Machine" -+ PURPOSE "Provides hierarchical finite state machines." -+) -+qt_feature("statemachine-qml" PUBLIC -+ LABEL "StateMachine QML Type" -+ PURPOSE "Provides QML Type for Qt State Machine." -+ CONDITION QT_FEATURE_statemachine AND TARGET Qt::Qml -+) -+qt_feature("qeventtransition" PUBLIC -+ LABEL "Q(Mouse)EventTransition class" -+ PURPOSE "Provides QObject-specific transitions for Qt events." -+ CONDITION QT_FEATURE_statemachine AND TARGET Qt::Gui -+) -+ -+qt_configure_add_summary_section(NAME "Qt SCXML") -+qt_configure_add_summary_entry(ARGS "scxml") -+qt_configure_add_summary_entry(ARGS "scxml-qml") -+qt_configure_add_summary_entry(ARGS "scxml-ecmascriptdatamodel") -+qt_configure_end_summary_section() # end of "Qt SCXML" section -+ -+qt_configure_add_summary_section(NAME "Qt State Machine") -+qt_configure_add_summary_entry(ARGS "statemachine") -+qt_configure_add_summary_entry(ARGS "statemachine-qml") -+qt_configure_add_summary_entry(ARGS "qeventtransition") -+qt_configure_end_summary_section() # end of "Qt State Machine" section - - qt_extra_definition("QT_VERSION_STR" "\"${PROJECT_VERSION}\"" PUBLIC) - qt_extra_definition("QT_VERSION_MAJOR" ${PROJECT_VERSION_MAJOR} PUBLIC) -diff --git x/qtscxml/src/CMakeLists.txt y/qtscxml/src/CMakeLists.txt -index 5fbfbd9d495ba5a926b3a44380a04a60bc06d5ee..b3e1253632d34bec8b78bcc9d2d7141dc4bf6db5 100644 ---- x/qtscxml/src/CMakeLists.txt -+++ y/qtscxml/src/CMakeLists.txt -@@ -1,11 +1,21 @@ - # Copyright (C) 2022 The Qt Company Ltd. - # SPDX-License-Identifier: BSD-3-Clause - -+# Evaluate features to decide what to build. -+# The config files will be written in the src/global module. -+qt_feature_evaluate_features("${CMAKE_CURRENT_SOURCE_DIR}/../configure.cmake") - --add_subdirectory(scxml) --add_subdirectory(statemachine) --if(TARGET Qt::Qml) -- add_subdirectory(statemachineqml) -- add_subdirectory(scxmlqml) -+add_subdirectory(global) -+if(QT_FEATURE_scxml) -+ add_subdirectory(scxml) -+ if(QT_FEATURE_scxml_qml) -+ add_subdirectory(scxmlqml) -+ endif() -+endif() -+if(QT_FEATURE_statemachine) -+ add_subdirectory(statemachine) -+ if(QT_FEATURE_statemachine_qml) -+ add_subdirectory(statemachineqml) -+ endif() - endif() - add_subdirectory(plugins) -diff --git x/qtscxml/src/global/CMakeLists.txt y/qtscxml/src/global/CMakeLists.txt -new file mode 100644 -index 0000000000000000000000000000000000000000..4ee272e1995f469bab80936da9227a63b85ee3f8 ---- /dev/null -+++ y/qtscxml/src/global/CMakeLists.txt -@@ -0,0 +1,15 @@ -+# Copyright (C) 2025 The Qt Company Ltd. -+# SPDX-License-Identifier: BSD-3-Clause -+ -+##################################################################### -+## ScxmlGlobal Module: -+##################################################################### -+ -+qt_internal_add_module(ScxmlGlobal -+ INTERNAL_MODULE -+ HEADER_MODULE -+ CONFIGURE_FILE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../configure.cmake" -+ MODULE_INTERFACE_NAME ScxmlGlobal # Specify the 'ScxmlGlobal' name explicitly to avoid -+ # warning related to the expected name of internal module. -+ NO_GENERATE_CPP_EXPORTS -+) -diff --git x/qtscxml/src/scxml/CMakeLists.txt y/qtscxml/src/scxml/CMakeLists.txt -index ee5b7431bda73f0281ef975294d87808d20b64e7..e4aa6290807fcbaed8570903b4b1fad102e284a0 100644 ---- x/qtscxml/src/scxml/CMakeLists.txt -+++ y/qtscxml/src/scxml/CMakeLists.txt -@@ -30,6 +30,7 @@ qt_internal_add_module(Scxml - QT_NO_CAST_TO_ASCII - LIBRARIES - Qt::CorePrivate -+ Qt::ScxmlGlobal - PUBLIC_LIBRARIES - Qt::Core - PRIVATE_MODULE_INTERFACE -diff --git x/qtscxml/src/scxml/configure.cmake y/qtscxml/src/scxml/configure.cmake -deleted file mode 100644 -index 55fc1d5a800e4855d0658ced9f0e609623705e09..0000000000000000000000000000000000000000 ---- x/qtscxml/src/scxml/configure.cmake -+++ /dev/null -@@ -1,28 +0,0 @@ --# Copyright (C) 2022 The Qt Company Ltd. --# SPDX-License-Identifier: BSD-3-Clause -- -- -- --#### Inputs -- -- -- --#### Libraries -- -- -- --#### Tests -- -- -- --#### Features -- --qt_feature("scxml-ecmascriptdatamodel" PUBLIC -- SECTION "SCXML" -- LABEL "ECMAScript data model for QtScxml" -- PURPOSE "Enables the usage of ecmascript data models in SCXML state machines." -- CONDITION TARGET Qt::Qml # special case --) --qt_configure_add_summary_section(NAME "Qt Scxml") --qt_configure_add_summary_entry(ARGS "scxml-ecmascriptdatamodel") --qt_configure_end_summary_section() # end of "Qt Scxml" section -diff --git x/qtscxml/src/scxml/qscxmlglobals.h y/qtscxml/src/scxml/qscxmlglobals.h -index 64169d240766d04f913294906accc6c1e6455429..9a3f6db8a6bf3a8eea9d937b6def448c6fd60d78 100644 ---- x/qtscxml/src/scxml/qscxmlglobals.h -+++ y/qtscxml/src/scxml/qscxmlglobals.h -@@ -4,7 +4,7 @@ - #ifndef QSCXMLGLOBALS_H - #define QSCXMLGLOBALS_H - #include --#include -+#include - - #if defined(BUILD_QSCXMLC) - # define Q_SCXML_EXPORT -diff --git x/qtscxml/src/statemachine/CMakeLists.txt y/qtscxml/src/statemachine/CMakeLists.txt -index e68c5718e245f5fb24a3b0629f315d4d3a8a0507..a225dbad8f81edcbfe99618ab821677cebe3bd3a 100644 ---- x/qtscxml/src/statemachine/CMakeLists.txt -+++ y/qtscxml/src/statemachine/CMakeLists.txt -@@ -24,6 +24,7 @@ qt_internal_add_module(StateMachine - QT_NO_CAST_TO_ASCII - LIBRARIES - Qt::CorePrivate -+ Qt::ScxmlGlobal - PUBLIC_LIBRARIES - Qt::Core - PRIVATE_MODULE_INTERFACE -diff --git x/qtscxml/src/statemachine/configure.cmake y/qtscxml/src/statemachine/configure.cmake -deleted file mode 100644 -index c6cda823037166fd3ebd120b214f296d08ba1386..0000000000000000000000000000000000000000 ---- x/qtscxml/src/statemachine/configure.cmake -+++ /dev/null -@@ -1,29 +0,0 @@ --# Copyright (C) 2022 The Qt Company Ltd. --# SPDX-License-Identifier: BSD-3-Clause -- -- -- --#### Inputs -- -- -- --#### Libraries -- -- -- --#### Tests -- -- -- --#### Features -- --qt_feature("statemachine" PUBLIC -- SECTION "Utilities" -- LABEL "State machine" -- PURPOSE "Provides hierarchical finite state machines." --) --qt_feature_definition("statemachine" "QT_NO_STATEMACHINE" NEGATE VALUE "1") --qt_feature("qeventtransition" PUBLIC -- LABEL "QEventTransition class" -- CONDITION QT_FEATURE_statemachine AND TARGET Qt::Gui # special case --) -diff --git x/qtscxml/src/statemachine/qstatemachineglobal.h y/qtscxml/src/statemachine/qstatemachineglobal.h -index 47860b7a4801f397bdd5464b5ab1b72510b25873..cb50c84cb5aa17eca0795ed727333dd941dbeb6d 100644 ---- x/qtscxml/src/statemachine/qstatemachineglobal.h -+++ y/qtscxml/src/statemachine/qstatemachineglobal.h -@@ -5,7 +5,7 @@ - #define QSTATEMACHINEGLOBAL_H - - #include --#include -+#include - - #if defined(BUILD_QSTATEMACHINE) - # define Q_STATEMACHINE_EXPORT -diff --git x/qtscxml/tools/CMakeLists.txt y/qtscxml/tools/CMakeLists.txt -index c5831a40460daefec3845fa34a2757b0232511fd..1497f1c25383fdc89dd8e2301d79b331fbf9c2d8 100644 ---- x/qtscxml/tools/CMakeLists.txt -+++ y/qtscxml/tools/CMakeLists.txt -@@ -2,6 +2,6 @@ - # SPDX-License-Identifier: BSD-3-Clause - - --if(QT_FEATURE_commandlineparser) -+if(QT_FEATURE_scxml AND QT_FEATURE_commandlineparser) - add_subdirectory(qscxmlc) - endif() diff -Nru ausweisapp2-2.3.1/libs/patches/qtsvg-0001-Replace-check-for-endless-recursion-when-loading.patch ausweisapp2-2.4.0/libs/patches/qtsvg-0001-Replace-check-for-endless-recursion-when-loading.patch --- ausweisapp2-2.3.1/libs/patches/qtsvg-0001-Replace-check-for-endless-recursion-when-loading.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtsvg-0001-Replace-check-for-endless-recursion-when-loading.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,104 @@ +From 7285a09bfc3bcca8a72ac795acae302afa8f9f55 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Robert=20L=C3=B6hning?= +Date: Wed, 18 Jun 2025 16:02:32 +0200 +Subject: Replace check for endless recursion when loading + +The old check parsed the tree of SvgNodes again and again which lead to +quadratic complexity. Instead, set and check a bool where the recursion +may actually happen which is faster and only has linear complexity. + +Partially reverts 0332df304f013ded362537c1f61556098b875352 + +I chose to have the check in QSvgPattern::renderPattern() because: + +- It not only appears in the recursive backtrace of the stack-overflow + which was fixed using the qudratic check, but also in the backtrace + of another, still unfixed stack overflow. That way, both can be fixed + by the same patch. Credit to OSS-Fuzz for finding them. +- The function already had some error checking and returns a default + value when it cannot render the content. In the same way, I can return + a QImage of the right size but without any content when the endless + recursion is about to happen. + +[ChangeLog] Speed up loading by replacing check for cyclic elements +[ChangeLog] Fix stack overflow when an element references its child +element using url() + +Fixes: QTBUG-137553 +Pick-to: 6.10 6.9 6.8 +Change-Id: If011c15fde50dcefeb653d1d5995ff1347e7b5ac +Reviewed-by: Hatem ElKharashy +(cherry picked from commit 9e5bed9584ab65d56cd5fbac0471e06e37a54412) +--- + src/svg/qsvghandler.cpp | 3 +-- + src/svg/qsvgstructure.cpp | 8 ++++++++ + src/svg/qsvgstructure_p.h | 1 + + tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp | 3 +++ + 4 files changed, 13 insertions(+), 2 deletions(-) + +diff --git x/qtsvg/src/svg/qsvghandler.cpp y/qtsvg/src/svg/qsvghandler.cpp +index 48f9f0b25c01404398857b6cbf1935a64f0dd861..c871a20cdc722e7feab6dd3273da708140f54f7c 100644 +--- x/qtsvg/src/svg/qsvghandler.cpp ++++ y/qtsvg/src/svg/qsvghandler.cpp +@@ -4647,8 +4647,7 @@ void QSvgHandler::parse() + // this point is to do what everyone else seems to do and + // ignore the reported namespaceUri completely. + if (remainingUnfinishedElements +- && startElement(xml->name().toString(), xml->attributes()) +- && !detectCyclesAndWarn(m_doc)) { ++ && startElement(xml->name().toString(), xml->attributes())) { + --remainingUnfinishedElements; + } else { + delete m_doc; +diff --git x/qtsvg/src/svg/qsvgstructure.cpp y/qtsvg/src/svg/qsvgstructure.cpp +index e08f1bc16f0d28281a2b0c382711edf8e030edb7..d073b1c4a04a377dff99ebbd5f22d3d4fc299ea4 100644 +--- x/qtsvg/src/svg/qsvgstructure.cpp ++++ y/qtsvg/src/svg/qsvgstructure.cpp +@@ -800,6 +800,7 @@ QSvgPattern::QSvgPattern(QSvgNode *parent, QSvgRectF bounds, QRectF viewBox, + m_rect(bounds), + m_viewBox(viewBox), + m_contentUnits(contentUnits), ++ m_isRendering(false), + m_transform(transform) + + { +@@ -885,6 +886,13 @@ QImage QSvgPattern::renderPattern(QSize size, qreal contentScaleX, qreal content + } + pattern.fill(Qt::transparent); + ++ if (m_isRendering) { ++ qCWarning(lcSvgDraw) << "The pattern is trying to render itself recursively. " ++ "Returning a transparent QImage of the right size."; ++ return pattern; ++ } ++ QScopedValueRollback guard(m_isRendering, true); ++ + // Draw the pattern using our QPainter. + QPainter patternPainter(&pattern); + QSvgExtraStates patternStates; +diff --git x/qtsvg/src/svg/qsvgstructure_p.h y/qtsvg/src/svg/qsvgstructure_p.h +index 63a839641db6b51442781688ad2c2713f63847f7..f95508f49c4561e847ce50b9c7a8d088935d5cb6 100644 +--- x/qtsvg/src/svg/qsvgstructure_p.h ++++ y/qtsvg/src/svg/qsvgstructure_p.h +@@ -241,6 +241,7 @@ private: + QSvgRectF m_rect; + QRectF m_viewBox; + QtSvg::UnitTypes m_contentUnits; ++ mutable bool m_isRendering; + QTransform m_transform; + }; + +diff --git x/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp y/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +index b212ad39a77e5ebd5b6690723c664a208f7068ce..56f6664328ead7c390d2b0c724566c8160bdcd8e 100644 +--- x/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp ++++ y/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +@@ -1809,6 +1809,9 @@ void tst_QSvgRenderer::ossFuzzLoad_data() + // resulted in stack overflow + QTest::newRow("cyclic-reference") // id=42532991 + << R"-()-"_ba; ++ // resulted in stack overflow ++ QTest::newRow("cyclic-reference-from-parent") // id=390467765 ++ << R"-()-"_ba; + } + + void tst_QSvgRenderer::ossFuzzLoad() diff -Nru ausweisapp2-2.3.1/libs/patches/qtsvg-0002-Don-t-create-group-nodes-which-will-be-deleted-anywa.patch ausweisapp2-2.4.0/libs/patches/qtsvg-0002-Don-t-create-group-nodes-which-will-be-deleted-anywa.patch --- ausweisapp2-2.3.1/libs/patches/qtsvg-0002-Don-t-create-group-nodes-which-will-be-deleted-anywa.patch 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches/qtsvg-0002-Don-t-create-group-nodes-which-will-be-deleted-anywa.patch 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,147 @@ +From 2069c10002d319635cfb3a1a54a9a4875be75c3d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Robert=20L=C3=B6hning?= +Date: Fri, 12 Sep 2025 21:22:58 +0200 +Subject: Don't create group nodes which will be deleted anyway + +The old code first created the nodes, then checked whether their parent +element has the right type and deleted them if not. This was wasted +effort and could also lead to dangling pointers. + +Instead, first check the parent's type and only create the node if that +matches. + +Task-number: QTBUG-139961 +Pick-to: 6.10.0 6.10 6.9 6.8 +Change-Id: Ifa870efbd5f336b34b81aa09b6fe79fb7fc826b9 +Reviewed-by: Hatem ElKharashy +(cherry picked from commit 7e8898903265d931df0aa54b3913f2c49d4d7bf2) +--- + src/svg/qsvghandler.cpp | 60 ++++++++++---------- + tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp | 26 +++++++++ + 2 files changed, 56 insertions(+), 30 deletions(-) + +diff --git x/qtsvg/src/svg/qsvghandler.cpp y/qtsvg/src/svg/qsvghandler.cpp +index c871a20cdc722e7feab6dd3273da708140f54f7c..edbad89803a0078b771e16304be987c84a12bdea 100644 +--- x/qtsvg/src/svg/qsvghandler.cpp ++++ y/qtsvg/src/svg/qsvghandler.cpp +@@ -4717,46 +4717,46 @@ bool QSvgHandler::startElement(const QString &localName, + + if (FactoryMethod method = findGroupFactory(localName, options())) { + //group +- node = method(m_doc ? m_nodes.top() : 0, attributes, this); +- +- if (node) { +- if (!m_doc) { ++ if (!m_doc) { ++ node = method(nullptr, attributes, this); ++ if (node) { + Q_ASSERT(node->type() == QSvgNode::Doc); + m_doc = static_cast(node); +- } else { +- switch (m_nodes.top()->type()) { +- case QSvgNode::Doc: +- case QSvgNode::Group: +- case QSvgNode::Defs: +- case QSvgNode::Switch: +- case QSvgNode::Mask: +- case QSvgNode::Symbol: +- case QSvgNode::Marker: +- case QSvgNode::Pattern: +- { ++ } ++ } else { ++ switch (m_nodes.top()->type()) { ++ case QSvgNode::Doc: ++ case QSvgNode::Group: ++ case QSvgNode::Defs: ++ case QSvgNode::Switch: ++ case QSvgNode::Mask: ++ case QSvgNode::Symbol: ++ case QSvgNode::Marker: ++ case QSvgNode::Pattern: ++ { ++ node = method(m_nodes.top(), attributes, this); ++ if (node) { + QSvgStructureNode *group = + static_cast(m_nodes.top()); + group->addChild(node, someId(attributes)); + } +- break; +- default: +- const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect."); +- qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData()); +- delete node; +- node = 0; +- break; +- } + } ++ break; ++ default: ++ const QByteArray msg = QByteArrayLiteral("Could not add child element to parent element because the types are incorrect."); ++ qCWarning(lcSvgHandler, "%s", prefixMessage(msg, xml).constData()); ++ break; ++ } ++ } + +- if (node) { +- parseCoreNode(node, attributes); ++ if (node) { ++ parseCoreNode(node, attributes); + #ifndef QT_NO_CSSPARSER +- cssStyleLookup(node, this, m_selector); ++ cssStyleLookup(node, this, m_selector); + #endif +- parseStyle(node, attributes, this); +- if (node->type() == QSvgNode::Filter) +- m_toBeResolved.append(node); +- } ++ parseStyle(node, attributes, this); ++ if (node->type() == QSvgNode::Filter) ++ m_toBeResolved.append(node); + } + } else if (FactoryMethod method = findGraphicsFactory(localName, options())) { + //rendering element +diff --git x/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp y/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +index 56f6664328ead7c390d2b0c724566c8160bdcd8e..9886d5558c5e6756d1a6f1e47bf11922af556814 100644 +--- x/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp ++++ y/qtsvg/tests/auto/qsvgrenderer/tst_qsvgrenderer.cpp +@@ -81,6 +81,7 @@ private slots: + void testSymbol(); + void testMarker(); + void testPatternElement(); ++ void testMisplacedElement(); + void testCycles(); + void testFeFlood(); + void testFeOffset(); +@@ -2127,6 +2128,31 @@ void tst_QSvgRenderer::testPatternElement() + QCOMPARE(refImage, image); + } + ++void tst_QSvgRenderer::testMisplacedElement() ++{ ++ // This input caused a QSvgPattern node to be created with a QSvgPatternStyle referencing to it. ++ // The code then detected that the element is misplaced in the element and ++ // deleted it. That left behind the QSvgPatternStyle pointing to the deleted QSvgPattern. That ++ // was reported when running the test with ASAN or UBSAN. ++ QByteArray svg(R"( ++ ++ ++ )"); ++ ++ QImage image(20, 20, QImage::Format_ARGB32_Premultiplied); ++ image.fill(Qt::green); ++ QImage refImage = image.copy(); ++ ++ QTest::ignoreMessage(QtWarningMsg, " ++ ++ Character by character ++ Zeichenweise ++ ++ ++ Word by word ++ Wortweise ++ ++ ++ Sentence by sentence ++ Satzweise ++ + + + QFactoryLoader diff -Nru ausweisapp2-2.3.1/libs/patches.cmake ausweisapp2-2.4.0/libs/patches.cmake --- ausweisapp2-2.3.1/libs/patches.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/patches.cmake 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.19.0) +cmake_minimum_required(VERSION 3.25) # How to use # @@ -6,14 +6,14 @@ # REPOSITORY_DIR as environment variable. # # - Apply existing patches -# $ cmake -DCMD=apply -P libs/patches.cmake +# $ cmake -P libs/patches.cmake apply # 1. This will clone all repositores to REPOSITORY_DIR. # 2. Checkout git tag from Versions.cmake to a new ausweisap_ branch. # It will delete that branch if it exist. # 3. Apply all patches to the new branch. # # - Generate patches from repositories -# $ cmake -DCMD=generate -P libs/patches.cmake +# $ cmake -P libs/patches.cmake generate # 1. All existing patches in "patches" directory will be deleted. # 2. Branch of current Version on all repositories in REPOSITORY_DIR will be scanned. # 3. All changesets from the latest tag will be exported to "patches" dir. @@ -31,8 +31,28 @@ # 4. git checkout -b ausweisapp_6.6.0 # 5. Bump version in Versions.cmake and use this script to generate the patches. -if(NOT CMAKE_SCRIPT_MODE_FILE OR NOT CMD) - message(FATAL_ERROR "Usage: cmake -DCMD=apply|generate -P libs/patches.cmake") +function(GET_PARAMS _params) + foreach(_arg RANGE ${CMAKE_ARGC}) + if("${CMAKE_ARGV${_arg}}" STREQUAL "-P") + math(EXPR delimiter "${_arg}+2") + break() + endif() + endforeach() + + if(DEFINED delimiter) + foreach(_arg RANGE ${delimiter} ${CMAKE_ARGC}) + list(APPEND param ${CMAKE_ARGV${_arg}}) + endforeach() + set(${_params} "${param}" PARENT_SCOPE) + endif() +endfunction() + +GET_PARAMS(parameter) +set(options apply generate) +cmake_parse_arguments(_PARAM "${options}" "${oneValueArgs}" "${multiValueArgs}" ${parameter}) + +if(NOT CMAKE_SCRIPT_MODE_FILE OR _PARAM_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Usage: cmake -P libs/patches.cmake apply|generate") endif() find_package(Git REQUIRED) @@ -67,12 +87,31 @@ endfunction() function(git_clone prefix) - if(NOT EXISTS "${REPOSITORY_DIR}/${prefix}") + set(REPOSITORY_PATH "${REPOSITORY_DIR}/${prefix}") + if(NOT EXISTS "${REPOSITORY_PATH}/.git") message(STATUS "Repository not found: ${prefix}") + if(EXISTS "${REPOSITORY_PATH}") + message(STATUS "Folder exists and will be removed: ${prefix}") + file(REMOVE_RECURSE "${REPOSITORY_PATH}") + endif() + if(prefix STREQUAL "openssl") execute(clone "https://github.com/openssl/openssl" "${prefix}") elseif(prefix MATCHES "qt") + set(QT_GIT_HOOKS_URL "https://code.qt.io/cgit/qt/qtrepotools.git/plain/git-hooks") execute(clone "https://code.qt.io/qt/${prefix}.git" "${prefix}") + execute_dir(${prefix} remote add gerrit "ssh://codereview.qt-project.org/qt/${prefix}") + file(DOWNLOAD "${QT_GIT_HOOKS_URL}/gerrit_commit_msg_hook" "${REPOSITORY_PATH}/.git/hooks/commit-msg") + file(DOWNLOAD "${QT_GIT_HOOKS_URL}/git_post_commit_hook" "${REPOSITORY_PATH}/.git/hooks/post-commit") + file(DOWNLOAD "${QT_GIT_HOOKS_URL}/sanitize-commit" "${REPOSITORY_PATH}/.git/hooks/sanitize-commit") + file(DOWNLOAD "${QT_GIT_HOOKS_URL}/clang-format-pre-commit" "${REPOSITORY_PATH}/.git/hooks/pre-commit") + file(CHMOD + "${REPOSITORY_PATH}/.git/hooks/commit-msg" + "${REPOSITORY_PATH}/.git/hooks/post-commit" + "${REPOSITORY_PATH}/.git/hooks/sanitize-commit" + "${REPOSITORY_PATH}/.git/hooks/pre-commit" + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + ) endif() endif() endfunction() @@ -192,6 +231,10 @@ foreach(prefix ${prefixes}) file(GLOB REPOS "${REPOSITORY_DIR}/${prefix}*") foreach(repo ${REPOS}) + if(NOT EXISTS "${repo}/.git") + message(STATUS "Not a repository: ${repo}") + continue() + endif() get_filename_component(dirname "${repo}" NAME) get_version_branch("${prefix}" version tmp_branch) execute_process(COMMAND ${GIT_EXECUTABLE} checkout -q ${tmp_branch} WORKING_DIRECTORY ${repo} OUTPUT_QUIET ERROR_QUIET) @@ -216,13 +259,12 @@ endfunction() +if(_PARAM_apply) + message(STATUS "Apply patches!") + apply_patches() +endif() -if(CMD STREQUAL "generate") +if(_PARAM_generate) message(STATUS "Generate patches!") generate_patches() -elseif(CMD STREQUAL "apply") - message(STATUS "Apply patches!") - apply_patches() -else() - message(FATAL_ERROR "Unknown CMD: ${CMD}") endif() diff -Nru ausweisapp2-2.3.1/libs/qt-install.qs ausweisapp2-2.4.0/libs/qt-install.qs --- ausweisapp2-2.3.1/libs/qt-install.qs 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/qt-install.qs 1970-01-01 00:00:00.000000000 +0000 @@ -1,98 +0,0 @@ -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 -Nru ausweisapp2-2.3.1/libs/qt.cmake ausweisapp2-2.4.0/libs/qt.cmake --- ausweisapp2-2.3.1/libs/qt.cmake 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/qt.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -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 -Nru ausweisapp2-2.3.1/libs/test/valgrind.supp ausweisapp2-2.4.0/libs/test/valgrind.supp --- ausweisapp2-2.3.1/libs/test/valgrind.supp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/test/valgrind.supp 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -{ - FICLONE: https://bugs.kde.org/show_bug.cgi?id=397605 - Memcheck:Param - ioctl(generic) - fun:ioctl - obj:/*/libQt5Core.so.* - fun:_ZN5QFile4copyERK7QString - fun:_ZN5QFile4copyERK7QStringS2_ -} - -{ - Platform plugin libqoffscreen - Memcheck:Leak - match-leak-kinds: definite - fun:_Znwm - obj:/*/plugins/platforms/libqoffscreen.so -} - -{ - https://bugreports.qt.io/browse/QTBUG-85180 - Memcheck:Leak - match-leak-kinds: definite - fun:_Znwm - obj:/*/lib/libQt5WebSockets.so.* - fun:_ZN17QTcpServerPrivate16readNotificationEv - obj:/*/lib/libQt5Network.so.* - fun:_ZN16QCoreApplication15notifyInternal2EP7QObjectP6QEvent - obj:/*/lib/libQt5Core.so.* - fun:g_main_context_dispatch -} - -{ - Internal of QProcess - Memcheck:Param - waitid(infop) - fun:syscall - obj:/*/libQt5Core.so.* -} - -{ - Invalid read of size 16 - Memcheck:Addr16 - obj:* - obj:* -} - -{ - fontconfig and XML_ParseBuffer (expat) - Memcheck:Leak - match-leak-kinds: definite - fun:malloc - obj:/*/libfontconfig.so.* -} diff -Nru ausweisapp2-2.3.1/libs/test/valgrind.supp.DEBUG ausweisapp2-2.4.0/libs/test/valgrind.supp.DEBUG --- ausweisapp2-2.3.1/libs/test/valgrind.supp.DEBUG 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/test/valgrind.supp.DEBUG 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -{ - FICLONE: https://bugs.kde.org/show_bug.cgi?id=397605 - Memcheck:Param - ioctl(generic) - fun:ioctl - fun:_ZN17QFileSystemEngine9cloneFileEiiRK19QFileSystemMetaData - fun:_ZN13QFSFileEngine7cloneToEP19QAbstractFileEngine - fun:_ZN5QFile4copyERK7QString - fun:_ZN5QFile4copyERK7QStringS2_ -} - -{ - Platform plugin libqoffscreen - Memcheck:Leak - match-leak-kinds: definite - fun:_Znwm - obj:/*/plugins/platforms/libqoffscreen.so -} - -{ - Internal of QProcess - Memcheck:Param - waitid(infop) - fun:syscall - fun:_ZL10sys_waitidiiP9siginfo_tiP6rusage - fun:_ZL26detect_clone_pidfd_supportv - fun:_ZL13system_forkfdiPiS_ - fun:forkfd - fun:_ZN15QProcessPrivate12startProcessEv - fun:_ZN15QProcessPrivate5startE6QFlagsIN9QIODevice12OpenModeFlagEE - fun:_ZN8QProcess5startE6QFlagsIN9QIODevice12OpenModeFlagEE -} diff -Nru ausweisapp2-2.3.1/libs/test/valgrind.supp.RELEASE ausweisapp2-2.4.0/libs/test/valgrind.supp.RELEASE --- ausweisapp2-2.3.1/libs/test/valgrind.supp.RELEASE 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/test/valgrind.supp.RELEASE 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -{ - FICLONE: https://bugs.kde.org/show_bug.cgi?id=397605 - Memcheck:Param - ioctl(generic) - fun:ioctl - obj:/*/libQt5Core.so.* - fun:_ZN5QFile4copyERK7QString - fun:_ZN5QFile4copyERK7QStringS2_ -} - -{ - Platform plugin libqoffscreen - Memcheck:Leak - match-leak-kinds: definite - fun:_Znwm - obj:/*/plugins/platforms/libqoffscreen.so -} - -{ - Internal of QProcess - Memcheck:Param - waitid(infop) - fun:syscall - obj:/*/libQt5Core.so.* -} - -{ - Invalid read of size 16 - Memcheck:Addr16 - obj:* - obj:* -} diff -Nru ausweisapp2-2.3.1/libs/test/valgrind.supp.RELWITHDEBINFO ausweisapp2-2.4.0/libs/test/valgrind.supp.RELWITHDEBINFO --- ausweisapp2-2.3.1/libs/test/valgrind.supp.RELWITHDEBINFO 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/libs/test/valgrind.supp.RELWITHDEBINFO 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -{ - FICLONE: https://bugs.kde.org/show_bug.cgi?id=397605 - Memcheck:Param - ioctl(generic) - fun:ioctl - fun:_ZN17QFileSystemEngine9cloneFileEiiRK19QFileSystemMetaData - fun:_ZN5QFile4copyERK7QString - fun:_ZN5QFile4copyERK7QStringS2_ -} - -{ - Platform plugin libqoffscreen - Memcheck:Leak - match-leak-kinds: definite - fun:_Znwm - obj:/*/plugins/platforms/libqoffscreen.so -} - -{ - Internal of QProcess - Memcheck:Param - waitid(infop) - fun:syscall - fun:sys_waitid - fun:detect_clone_pidfd_support - fun:system_forkfd - fun:forkfd - fun:_ZN15QProcessPrivate12startProcessEv -} - -{ - Invalid read of size 16 - Memcheck:Addr16 - obj:* - obj:* -} diff -Nru ausweisapp2-2.3.1/presets/ci-android.json ausweisapp2-2.4.0/presets/ci-android.json --- ausweisapp2-2.3.1/presets/ci-android.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/presets/ci-android.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -{ - "version": 6, - "include": [ - "ci.json" - ], - "configurePresets": [ - { - "name": "ci-android", - "hidden": true, - "inherits": "ci-with-libs", - "toolchainFile": "${sourceDir}/cmake/android.toolchain.cmake" - }, - { - "name": "ci-android-apk", - "inherits": "ci-android", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "MinSizeRel" - } - }, - { - "name": "ci-android-apk-review", - "inherits": "ci-android", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "DEBUG" - } - }, - { - "name": "ci-android-aar", - "inherits": "ci-android", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "MinSizeRel", - "INTEGRATED_SDK": "ON" - } - }, - { - "name": "ci-android-aar-review", - "inherits": "ci-android", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "DEBUG", - "INTEGRATED_SDK": "ON" - } - } - ] -} diff -Nru ausweisapp2-2.3.1/presets/ci-bsd.json ausweisapp2-2.4.0/presets/ci-bsd.json --- ausweisapp2-2.3.1/presets/ci-bsd.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/presets/ci-bsd.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -{ - "version": 6, - "include": [ - "ci-linux.json" - ], - "configurePresets": [ - { - "name": "ci-bsd", - "inherits": "ci-linux" - } - ] -} diff -Nru ausweisapp2-2.3.1/presets/ci-iOS.json ausweisapp2-2.4.0/presets/ci-iOS.json --- ausweisapp2-2.3.1/presets/ci-iOS.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/presets/ci-iOS.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -{ - "version": 6, - "include": [ - "ci.json" - ], - "configurePresets": [ - { - "name": "ci-ios", - "inherits": "ci-with-libs", - "generator": "Xcode", - "toolchainFile": "${sourceDir}/cmake/iOS.toolchain.cmake", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "MinSizeRel" - } - }, - { - "name": "ci-ios-framework", - "inherits": "ci-ios", - "cacheVariables": { - "INTEGRATED_SDK": "ON" - } - }, - { - "name": "ci-ios-framework-simulator", - "inherits": "ci-ios-framework", - "generator": "Xcode", - "cacheVariables": { - "CMAKE_OSX_SYSROOT": "iphonesimulator", - "CMAKE_OSX_ARCHITECTURES": "x86_64" - } - }, - { - "name": "ci-ios-framework-simulator-arm64", - "inherits": "ci-ios-framework-simulator", - "cacheVariables": { - "CMAKE_OSX_ARCHITECTURES": "arm64" - } - } - ] -} diff -Nru ausweisapp2-2.3.1/presets/ci-linux.json ausweisapp2-2.4.0/presets/ci-linux.json --- ausweisapp2-2.3.1/presets/ci-linux.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/presets/ci-linux.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -{ - "version": 6, - "include": [ - "ci.json" - ], - "configurePresets": [ - { - "name": "ci-linux", - "inherits": "ci-with-libs", - "cacheVariables": { - "COVERAGE": "ON", - "BUILD_SHARED_LIBS": "ON", - "SANITIZER": "ON" - } - }, - { - "name": "ci-integrated", - "inherits": "ci-linux", - "cacheVariables": { - "INTEGRATED_SDK": "ON", - "CMAKE_CXX_COMPILER": "clazy" - } - } - ] -} diff -Nru ausweisapp2-2.3.1/presets/ci-macOS.json ausweisapp2-2.4.0/presets/ci-macOS.json --- ausweisapp2-2.3.1/presets/ci-macOS.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/presets/ci-macOS.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -{ - "version": 6, - "include": [ - "ci.json" - ], - "configurePresets": [ - { - "name": "ci-macos", - "inherits": "ci-with-libs", - "generator": "Xcode", - "cacheVariables": { - "BUILD_SHARED_LIBS": "ON", - "SANITIZER": "ON" - } - }, - { - "name": "ci-macos-release", - "inherits": "ci-with-libs", - "generator": "Xcode", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "MinSizeRel" - } - }, - { - "name": "ci-macos-integrated", - "inherits": "ci-with-libs", - "cacheVariables": { - "INTEGRATED_SDK": "ON", - "SANITIZER": "ON" - } - } - ] -} diff -Nru ausweisapp2-2.3.1/presets/ci-tools.json ausweisapp2-2.4.0/presets/ci-tools.json --- ausweisapp2-2.3.1/presets/ci-tools.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/presets/ci-tools.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -{ - "version": 6, - "include": [ - "ci.json" - ], - "configurePresets": [ - { - "name": "ci-translations", - "inherits": "ci-with-libs", - "cacheVariables": { - "UPDATE_TRANSLATIONS": "ON", - "UPDATE_TRANSLATIONS_ADD_DVCS": "ON", - "UPDATE_TRANSLATIONS_NO_OBSOLETE": "ON" - } - }, - { - "name": "ci-tools", - "inherits": "ci", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "RELEASE", - "tools.only": "ON", - "CMAKE_CXX_COMPILER_LAUNCHER": null - } - }, - { - "name": "ci-tools-with-libs", - "inherits": "ci-tools", - "cacheVariables": { - "CMAKE_PREFIX_PATH": "${sourceParentDir}/libs/dist" - } - } - ] -} diff -Nru ausweisapp2-2.3.1/presets/ci-windows.json ausweisapp2-2.4.0/presets/ci-windows.json --- ausweisapp2-2.3.1/presets/ci-windows.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/presets/ci-windows.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -{ - "version": 6, - "include": [ - "ci.json" - ], - "configurePresets": [ - { - "name": "ci-win", - "inherits": "ci-with-libs" - }, - { - "name": "ci-win-release", - "inherits": "ci-win", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "MinSizeRel", - "WIN_SIGN_KEYSTORE": "$env{WIN_SIGN_KEYSTORE}", - "WIN_SIGN_KEYSTORE_PSW": "$env{WIN_SIGN_KEYSTORE_PSW}", - "WIN_SIGN_SUBJECT_NAME": "$env{WIN_SIGN_SUBJECT_NAME}" - } - }, - { - "name": "ci-win-debug", - "inherits": "ci-win", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "DEBUG", - "WIN_SIGN_KEYSTORE": "$env{WIN_SIGN_KEYSTORE}", - "WIN_SIGN_KEYSTORE_PSW": "$env{WIN_SIGN_KEYSTORE_PSW}", - "WIN_SIGN_SUBJECT_NAME": "$env{WIN_SIGN_SUBJECT_NAME}" - } - } - ] -} diff -Nru ausweisapp2-2.3.1/presets/ci.json ausweisapp2-2.4.0/presets/ci.json --- ausweisapp2-2.3.1/presets/ci.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/presets/ci.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -{ - "version": 6, - "configurePresets": [ - { - "name": "ci", - "hidden": true, - "generator": "Ninja", - "binaryDir": "${sourceParentDir}/build", - "cacheVariables": { - "CMAKE_CXX_COMPILER_LAUNCHER": "ccache" - }, - "errors": { - "dev": true, - "deprecated": true - } - }, - { - "name": "ci-with-libs", - "hidden": true, - "inherits": "ci", - "cacheVariables": { - "CMAKE_PREFIX_PATH": "${sourceParentDir}/libs/dist" - } - } - ] -} diff -Nru ausweisapp2-2.3.1/resources/CMakeLists.txt ausweisapp2-2.4.0/resources/CMakeLists.txt --- ausweisapp2-2.3.1/resources/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -15,23 +15,6 @@ WRITE_QRC("${_dest_file}" "${_dir}" "${_prefix}" "${files}") endfunction() -function(ADD_SHADERS_TO_TARGET _target) - if(TARGET ${Qt}::Qml AND NOT INTEGRATED_SDK) - file(GLOB QT_SHADERS "${RESOURCES_DIR}/shader/*.frag") - foreach(shaderPath ${QT_SHADERS}) - get_filename_component(shaderName ${shaderPath} NAME) - list(APPEND QT_SHADER_OUTPUTS ${shaderName}) - endforeach() - - qt_add_shaders(${_target} "${_target}_shader" - PRECOMPILE - PREFIX "/shader" - FILES "${QT_SHADERS}" - OUTPUTS "${QT_SHADER_OUTPUTS}" - QUIET) - endif() -endfunction() - if(EXISTS "${CMAKE_DIR}/SmartService.internal.cmake") include(SmartService.internal) @@ -48,20 +31,23 @@ list(APPEND QRC_FILES "${ausweisapp_config.qrc}") endif() +if(DESKTOP AND CMAKE_BUILD_TYPE STREQUAL "DEBUG") + set(FORCE_QRC ON) +endif() -if(NOT INTEGRATED_SDK OR BUILD_TESTING) +if(NOT INTEGRATED_SDK OR FORCE_QRC) list(APPEND QRC_FILES "ausweisapp.qrc") endif() -if((IOS OR ANDROID) AND NOT INTEGRATED_SDK OR BUILD_TESTING) +if((IOS OR ANDROID) AND NOT INTEGRATED_SDK OR FORCE_QRC) list(APPEND QRC_FILES "ausweisapp_mobile.qrc") endif() -if((DESKTOP AND NOT INTEGRATED_SDK) OR BUILD_TESTING) +if((DESKTOP AND NOT INTEGRATED_SDK) OR FORCE_QRC) list(APPEND QRC_FILES "ausweisapp_desktop.qrc") endif() -if((DESKTOP OR CONTAINER_SDK) OR BUILD_TESTING) +if(CONTAINER_SDK OR (DESKTOP AND NOT INTEGRATED_SDK) OR FORCE_QRC) list(APPEND QRC_FILES "ausweisapp_webservice.qrc") endif() diff -Nru ausweisapp2-2.3.1/resources/ausweisapp.qrc ausweisapp2-2.4.0/resources/ausweisapp.qrc --- ausweisapp2-2.3.1/resources/ausweisapp.qrc 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/ausweisapp.qrc 2025-10-30 10:10:48.000000000 +0000 @@ -81,8 +81,6 @@ images/phone_to_pc.svg images/signature.jp2 images/trash_icon.svg - shader/ColorOverlayShader.frag - shader/DesaturateShader.frag updatable-files/supported-providers.json diff -Nru ausweisapp2-2.3.1/resources/config.json.in ausweisapp2-2.4.0/resources/config.json.in --- ausweisapp2-2.3.1/resources/config.json.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/config.json.in 2025-10-30 10:10:48.000000000 +0000 @@ -45,12 +45,12 @@ "7F218201B67F4E82016E5F290100420E44455445535465494430303030327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410474FF63AB838C73C303AC003DFEE95CF8BF55F91E8FEBCB7395D942036E47CF1845EC786EC95BB453AAC288AD023B6067913CF9B63F908F49304E5CFC8B3050DD8701015F200E44455445535465494430303030347F4C12060904007F0007030102025305FC0F13FFFF5F25060102000501015F24060105000501015F37405C035A0611B6C58F0B5261FDD009DECAB7DC7A79482D5248CCA119059B7D82B2157CF0C4A499BCF441EFDD35E294A58C0AF19A34A0762159533285ACF170A505", "7F218201B67F4E82016E5F290100420E44455445535465494430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104096EB58BFD86252238EC2652185C43C3A56C320681A21E37A8E69DDC387C0C5F5513856EFE2FDC656E604893212E29449B365E304605AC5413E75BE31E641F128701015F200E44455445535465494430303030327F4C12060904007F0007030102025305FE0F01FFFF5F25060100000902015F24060103000902015F3740141120A0FDFC011A52F3F72B387A3DC7ACA88B4868D5AE9741780B6FF8A0B49E5F55169A2D298EF5CF95935DCA0C3DF3E9D42DC45F74F2066317154961E6C746", "7F218201B67F4E82016E5F290100420E44455445535465494430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104184BB519FC2A8F52DC0DC73112FACFE914F2A49B678DD5799A2B1DFE95E1A66359014E22FA8D66438413CEBA6CF0E215576B673376BF617AF4DFE9761D2290148701015F200E44455445535465494430303030317F4C12060904007F0007030102025305FE0F01FFFF5F25060100000801035F24060103000801035F37409F25EBFAF4B91E4C60A1683754C5DC076A3179753EF97D9F8CB01FE1DCD3B8C83E7A26602AB1F344BE5706006D79A9FF6A9716404DC83B9F30E1213B393128A2", - "7F218201BA7F4E8201725F290100421044454356434165494443544C303430347F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641046F3D366B7319581EE48D9D9EE273D4BC41E53BAB45358CD74E18AC9D602DEA993FA366E3061EF944B9C7CAD560CC6A8933925C8BC3359284923A873316D97FD98701015F201044454356434165494443544C303430357F4C12060904007F0007030102025305FE1FFFFFFF5F25060202010200075F24060205010200065F37407C2D73524FB2F9C393201C85338EA4BD8A818601C8D2750A17AAD690042DBA3C340AC69F907B0D82296BCD542A5DEAF26DC62FB481610A5690972613026E4C02", - "7F218201BA7F4E8201725F290100421044454356434165494443544C303430337F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410443836548E0D56977680A1B08BB205C98C835884205752C9E12C0DF001C9F55003F37772E0C6A0880129511CCE06CC71634F624EF45E61B975F5FEBF5B1A43F118701015F201044454356434165494443544C303430347F4C12060904007F0007030102025305FE1FFFFFFF5F25060202010200075F24060205010200065F374098AB56059D92DB9877238B0C6EF3DD42E2B52D60E3EBC7AB3A9C4D51388062D04FDA64EDDFD49B7C95D541CD5CB197D46B792B01386910A1F7DDF18C2BF94ABD", - "7F218201BA7F4E8201725F290100421044454356434165494443544C303430327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641043FD0CCB9B743EF5A7F55380A9CE2074300B8982D279BEA4BFD77C42ACC85F7BB6F8F3B88119546826DC712AF496178D154FE23064927E4ABB6B0B22DBE9BD6658701015F201044454356434165494443544C303430337F4C12060904007F0007030102025305FE1FFFFFFF5F25060202010200075F24060205010200065F374048CF7AEEE2D7669693390CE5885397099AE90F81852BF28FACDF4BDBB4DCEB2EA031FBCF2F495EAD4222C91194373AEA3081ABC45FAD306C0957FBEF866F37B9", - "7F218201BA7F4E8201725F290100421044454356434165494443544C303430317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641044633685F4AA11C433376C04EF7A7D9DB9B6E8717CBFADC257620D18CEA2CBD911C32133B2B91EB551D7315AE2875032F82445C4738800A3B65C5EC611162DAFC8701015F201044454356434165494443544C303430327F4C12060904007F0007030102025305FE1FFFFFFF5F25060202010200075F24060205010200065F374026DC0E7E15DB8EEF43781F6225F6CEBED27D71ECD6A9E27060DECCDF39913327609F08CCF7912BB4C0AA2EA2B7ADCEB07F1B9EB4C9701B61656597AE3D458C47", - "7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641046E7C1FD08E10D9A0D3FDDF61D0EB8076784E6EF61A1915B8E474BE91CD7562772C2F5560231F1A6819689433123CA17D12F43DA7B21CABF0317D2120D1309E288701015F201044454356434165494443544C303430317F4C12060904007F0007030102025305FE1FFFFFFF5F25060202010200075F24060205010200065F3740A9F770304FD2FE590799F2FCB109054E586092D1A021674C1BD87EDBE3FF622188D6C7D4D2669380FC4DEF700453FB6FDC7BD0C56E82318B37A2E468D407EED2", - "7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410486E5153975643814C32A934028AFA4402D4A0D732C60CFDDBC060842257D84DF358682D54333179114328448273577F65D9CC0232EA6F3B4F90D34EEA83991598701015F2010444543564341654944435430303030317F4C12060904007F0007030102025305FE1FFFFFFF5F25060202010200075F24060205010200065F3740445884C8A680B0490B1F4BF1117DC008156A309281CBBADB005AEC745F5F4E0E294321F2A0D339BA477C04F33670E25371768C15EBBE4C4D1D786977888612FB" + "7F218201BA7F4E8201725F290100421044454356434165494443544C303430347F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641043B7E8A709F0CCBFDA27FCCB90FD7B3D286EEE8A703B0BE5640F150F341227E5A5AFADA0100C8164EF0C4BED2E96953F5F25F14CE559E867717C10A6A3152D6188701015F201044454356434165494443544C303430357F4C12060904007F0007030102025305FE1FFFFFFF5F25060204010001055F24060207010001055F3740A0C356EFA852588594E8D99498BBCD2206244110CC896786F4D0C0329E526CB0061F580724689BE0936623E92015CBD6B582C903DDF66FD123F1CAC0071DBE3E", + "7F218201BA7F4E8201725F290100421044454356434165494443544C303430337F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104A8855521011CD5D94B0E53F52FCC7312E4ECDD5B8E7699FCE9FC114D4104BEF594C7024C9982630B4C64D9DA94B399137D4CA45F7A67C15D75C6AE7038987A038701015F201044454356434165494443544C303430347F4C12060904007F0007030102025305FE1FFFFFFF5F25060204010001055F24060207010001055F374050A64336CD8F218E8BA077BEFEC0523412641DACB3A2AC3E142A1350129F5E007EF013755D91812F17800109AC43E5EB0EF09A497C34052D88DEB03807E1C0C8", + "7F218201BA7F4E8201725F290100421044454356434165494443544C303430327F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641040A53970E9F5D82079F6B021201BE0F5A63A5B403F71A6D528A81BB5D56D11DF82A8E32438815D1CB8B76AF2733339B727D51C724B77DC29990DD68A6FBCAA6B08701015F201044454356434165494443544C303430337F4C12060904007F0007030102025305FE1FFFFFFF5F25060204010001055F24060207010001055F3740244C24F815A04A61E5B9BD13E7E0CB3F75743F718886453CE5966EF671E3916200C0B65295921F5FFF929DACBF6E608BA2EE4520C9F791B3D4B4436ECB449711", + "7F218201BA7F4E8201725F290100421044454356434165494443544C303430317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A786410425704CD0FFED45578892975482F29A8EA35844CA5DB754AF03CBA4434F5727D3247408A82C763BA2AC911B698E1090492AD03F8F0A83E7CAFA99B23A1CF66BBE8701015F201044454356434165494443544C303430327F4C12060904007F0007030102025305FE1FFFFFFF5F25060204010001055F24060207010001055F374043F89214D74FCC252A0662E943FC84163D502A9A2F3F12731361A09C4E3EE2D13CAB854F4BF8D5A14A942A58A6E79205E684D2BBAE2D590A8B0F07263F20EC7E", + "7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104193FE0A28D70CA1B2EB4B8382579BF5E8037BD610A3A5CBF8EE8BCF1D464EB90395CA63DC363830FCC3E9CC043F24C53E278BA330E7C22EA1D0D91CA0F6503588701015F201044454356434165494443544C303430317F4C12060904007F0007030102025305FE1FFFFFFF5F25060204010001055F24060207010001055F37400A7DF281972965502C7A3567E2BC898E68F162AA367ABC94527D159E680F3BDA59EC839547B26D53B5F9953EB617431A002352964555CD34EFA2B8347C68A740", + "7F218201BA7F4E8201725F2901004210444543564341654944435430303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641040FE7159464FAB35D209914EAACEE251E341A8683E2E7BA1487150AB5FC84B704524B2011015C0145455DF58F03C6AE64AD026673D6F2ECF45212DE5F59E8F5D38701015F2010444543564341654944435430303030317F4C12060904007F0007030102025305FE1FFFFFFF5F25060204010001055F24060207010001055F374007DF10B9311475AC4747C27BA544AE6E346DBB9028A573456768855FE593297F398B373AF29C473CD8DAAFBC374AAAD33B7B174A0865031DD1970864D993984E" ], "_comment_5": "array of certificates for checking the authenticity of updates; DER format, hex encoded", diff -Nru ausweisapp2-2.3.1/resources/jenkins/check_formatting.sh ausweisapp2-2.4.0/resources/jenkins/check_formatting.sh --- ausweisapp2-2.3.1/resources/jenkins/check_formatting.sh 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/check_formatting.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -#!/bin/sh - -if [ -z "$BUILD_URL" ]; then -echo "Warning: This script should be called on jenkins only" - exit 1 -fi - -if [ -n "$1" ]; then - PATCH=$1 -else - PATCH="../patch.diff" -fi - -REVISION_CURRENT=$(hg id -i) - -# Check if the current repository state is formatted -cmake --build ../build --target format -STATUS_CURRENT=$(hg status | wc -c) -if [ "$STATUS_CURRENT" != "0" ]; then - echo 'Current repository state is not formatted!' - hg addremove - hg diff - hg commit -m "fix formatting" -s -fi -REVISION_FORMATTED=$(hg id -i) - -if [ ! -f "$PATCH" ] && [ -n "$NO_PATCH" ]; then - if [ "$STATUS_CURRENT" != "0" ]; then - echo 'FORMATTING FAILED' - else - echo 'FORMATTING SUCCEDED' - fi - exit 0 -fi - -# Rollback any changes -hg update -C -r "$REVISION_CURRENT" - -# Apply patch on the current repository state -hg --config patch.eol=auto --config phases.new-commit=secret import -m 'jenkins patch formatting' -d 'today' -u 'jenkins' "$PATCH" -if [ "$?" != "0" ]; then - echo 'FORMATTING FAILED: Patch cannot be applied' - exit 0 -fi - -# Check if the repository state is formatted after the patch -cmake --build ../build --target rebuild_cache -cmake --build ../build --target format -STATUS_PATCH=$(hg status | wc -c) -if [ "$STATUS_PATCH" = "0" ]; then - if [ "$STATUS_CURRENT" != "0" ]; then - echo 'Patch fixes repository formatting.' - fi - echo 'FORMATTING SUCCEDED' - exit 0 -elif [ "$STATUS_CURRENT" = "0" ]; then - echo 'FORMATTING FAILED: Patch is not formatted' - hg diff - hg revert -a -C - exit 0 -fi - -# Checkout formatted repository state -hg update -C -r "$REVISION_FORMATTED" - -# Apply patch on the formatted repository state -hg --config patch.eol=auto --config phases.new-commit=secret import -m 'jenkins patch formatting' -d 'today' -u 'jenkins' "$PATCH" -if [ "$?" != "0" ]; then - echo 'FORMATTING FAILED: Patch cannot be applied, because the current repository state is unformatted and the patch conflicts with the formatted repository state without fixing the formatting!' - exit 0 -fi - -# Check if the patch itself is formatted even though is does not fix -# the unformatted repository state -cmake --build ../build --target rebuild_cache -cmake --build ../build --target format -STATUS_PATCH=$(hg status | wc -c) -if [ "$STATUS_PATCH" != "0" ]; then - echo 'FORMATTING FAILED: Patch is not formatted' - hg diff - hg revert -a -C - exit 0 -fi - -echo 'FORMATTING SUCCEDED: Patch does not introduce new formatting issues.' diff -Nru ausweisapp2-2.3.1/resources/jenkins/docker/Dockerfile ausweisapp2-2.4.0/resources/jenkins/docker/Dockerfile --- ausweisapp2-2.3.1/resources/jenkins/docker/Dockerfile 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/docker/Dockerfile 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -ARG ALPINE_VERSION=3.17 - -FROM alpine:$ALPINE_VERSION AS builder -# Install development stuff -RUN apk --no-cache upgrade -a && \ - apk --no-cache add patch cmake ccache make ninja g++ pkgconf pcsc-lite-dev binutils-gold eudev-libs \ - http-parser-dev openssl-dev \ - qt6-qtbase-dev qt6-qtsvg-dev qt6-qtwebsockets-dev qt6-qttools-dev qt6-qtdeclarative-dev qt6-qtscxml-dev qt6-qtconnectivity-dev qt6-qtimageformats-dev qt6-qtimageformats - -# Use optional remote ccache -# redis://YOUR_SERVER:6379 -ARG CCACHE_REMOTE_STORAGE="" -ENV CCACHE_REMOTE_STORAGE=$CCACHE_REMOTE_STORAGE CCACHE_REMOTE_ONLY=true CCACHE_RESHARE=true CCACHE_DIR=/build/ccache XDG_RUNTIME_DIR=/root - -# Build AusweisApp -COPY README.rst /src/ausweisapp/ -COPY LICENSE.txt/ /src/ausweisapp/ -COPY LICENSE.officially.txt/ /src/ausweisapp/ -COPY docs/ /src/ausweisapp/docs/ -COPY CMakeLists.txt /src/ausweisapp/ -COPY cmake/ /src/ausweisapp/cmake/ -COPY resources/ /src/ausweisapp/resources/ -COPY src/ /src/ausweisapp/src/ -COPY test/ /src/ausweisapp/test/ - -RUN cmake /src/ausweisapp -B /build/app \ - -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ - -DBUILD_SHARED_LIBS=ON \ - -GNinja && \ - cmake --build /build/app && \ - ctest --test-dir /build/app --output-on-failure -j `nproc` && \ - cmake --install /build/app && \ - ccache -s -vv && rm -rf /build - -RUN find /usr/local/ -type d -empty -delete && \ - find /usr/local/lib/ -type f -not -name "*.so*" -delete && \ - find /usr/local/lib/ -type f -name "*.so*" -exec strip {} + && \ - strip /usr/local/bin/AusweisApp - - - -FROM alpine:$ALPINE_VERSION -ENV XDG_RUNTIME_DIR=/home/ausweisapp QT_QPA_PLATFORM=vnc - -COPY --from=builder /usr/local/lib /usr/local/lib -COPY --from=builder /usr/local/share /usr/local/share -COPY --from=builder /usr/local/bin/AusweisApp /usr/local/bin/AusweisApp - -RUN apk --no-cache upgrade -a && \ - apk --no-cache add tini pcsc-lite-libs eudev-libs doas ttf-freefont \ - http-parser qt6-qtbase qt6-qtsvg qt6-qtwebsockets qt6-qtdeclarative qt6-qtscxml qt6-qtconnectivity qt6-qtimageformats && \ - echo 'permit nopass :wheel' > /etc/doas.d/wheel.conf && \ - adduser ausweisapp -G wheel -s /bin/sh -D && \ - chmod 0700 /home/ausweisapp && mkdir -p /home/ausweisapp/.config && chown ausweisapp: /home/ausweisapp/.config - -USER ausweisapp -VOLUME ["/home/ausweisapp/.config"] -ENTRYPOINT ["/sbin/tini", "--"] -EXPOSE 5900/tcp 24727/tcp 24727/udp -CMD ["AusweisApp", "--address", "0.0.0.0", "--no-logfile"] diff -Nru ausweisapp2-2.3.1/resources/jenkins/docker/alpine-common/Dockerfile ausweisapp2-2.4.0/resources/jenkins/docker/alpine-common/Dockerfile --- ausweisapp2-2.3.1/resources/jenkins/docker/alpine-common/Dockerfile 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/docker/alpine-common/Dockerfile 2025-10-30 10:10:48.000000000 +0000 @@ -1,20 +1,7 @@ FROM dev-docker.governikus.de/ausweisapp2/alpine:swarm -ARG UNCRUSTIFY=0.80.1 ENV NAME=Common LABELS=Common -RUN mkdir /build/ && \ - wget -O uncrustify.tar.gz https://github.com/uncrustify/uncrustify/archive/uncrustify-$UNCRUSTIFY.tar.gz && \ - tar xf uncrustify.tar.gz && \ - apk --no-cache --virtual deps add g++ samurai && \ - cmake uncrustify-uncrustify-$UNCRUSTIFY -GNinja -DCMAKE_BUILD_TYPE=Release -B build && \ - cmake --build build && \ - cmake --install build && \ - apk --no-cache del deps && \ - apk --no-cache add dos2unix && \ - rm -rf /build - - USER governikus ENTRYPOINT ["/sbin/tini", "--"] diff -Nru ausweisapp2-2.3.1/resources/jenkins/docker/alpine-docs/Dockerfile ausweisapp2-2.4.0/resources/jenkins/docker/alpine-docs/Dockerfile --- ausweisapp2-2.3.1/resources/jenkins/docker/alpine-docs/Dockerfile 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/docker/alpine-docs/Dockerfile 2025-10-30 10:10:48.000000000 +0000 @@ -2,7 +2,7 @@ ENV NAME=Docs LABELS=Docs -RUN apk --no-cache add py3-setuptools icu poppler zziplib enscript ghostscript texlive-full py3-sphinx py3-sphinx_rtd_theme py3-sphinx-copybutton py3-sphinxcontrib-tabs && \ +RUN apk --no-cache add py3-setuptools icu poppler zziplib enscript ghostscript texlive-full py3-sphinx py3-sphinx_rtd_theme py3-sphinx-copybutton py3-sphinxcontrib-tabs pandoc-cli && \ pip3 install --break-system-packages doc8 USER governikus diff -Nru ausweisapp2-2.3.1/resources/jenkins/docker/alpine-linux/Dockerfile ausweisapp2-2.4.0/resources/jenkins/docker/alpine-linux/Dockerfile --- ausweisapp2-2.3.1/resources/jenkins/docker/alpine-linux/Dockerfile 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/docker/alpine-linux/Dockerfile 2025-10-30 10:10:48.000000000 +0000 @@ -2,8 +2,8 @@ ENV NAME=Linux LABELS="Linux g++ clang++ clazy" PACKAGES_DIR=/home/governikus/packages -RUN apk --no-cache add g++ clang clang-analyzer ccache gcovr cloc clazy cppcheck pkgconf pcsc-lite-dev binutils-gold lld \ - py3-setuptools check-jsonschema mesa-dev libx11-dev libxkbcommon-dev xcb-util-wm-dev xcb-util-image-dev xcb-util-keysyms-dev && \ +RUN apk --no-cache add g++ clang clang-analyzer ccache gcovr cloc clazy cppcheck pkgconf pcsc-lite-dev binutils-gold lld uncrustify dos2unix typos yamllint ruff \ + py3-codespell py3-setuptools check-jsonschema 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 diff -Nru ausweisapp2-2.3.1/resources/jenkins/docker/alpine-swarm/Dockerfile ausweisapp2-2.4.0/resources/jenkins/docker/alpine-swarm/Dockerfile --- ausweisapp2-2.3.1/resources/jenkins/docker/alpine-swarm/Dockerfile 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/docker/alpine-swarm/Dockerfile 2025-10-30 10:10:48.000000000 +0000 @@ -1,10 +1,10 @@ -FROM alpine:3.21 +FROM alpine:3.22 ARG JENKINS_SWARM_VERSION=3.48 ENV EXECUTOR=3 LABELS= NAME= PASSWORD= RUN adduser governikus -s /bin/sh -D -RUN apk --no-cache add openjdk17-jre-headless cmake samurai make tini gnupg mercurial py3-pip wget rbtools && \ +RUN apk --no-cache add openjdk17-jre-headless cmake samurai make tini gnupg mercurial py3-pip wget rbtools uv && \ pip3 install --break-system-packages python-hglib RUN wget -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 diff -Nru ausweisapp2-2.3.1/resources/jenkins/docker/generate.py ausweisapp2-2.4.0/resources/jenkins/docker/generate.py --- ausweisapp2-2.3.1/resources/jenkins/docker/generate.py 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/docker/generate.py 2025-10-30 10:10:48.000000000 +0000 @@ -7,14 +7,20 @@ cur_version = sys.version_info if cur_version < req_version: - print("python version >=3.5 required") + print('python version >=3.5 required') sys.exit(1) args = str(sys.argv) # Alpine -alpine = ['alpine:swarm', 'alpine:trigger', 'alpine:sync', - 'alpine:common', 'alpine:docs', 'alpine:linux'] +alpine = [ + 'alpine:swarm', + 'alpine:trigger', + 'alpine:sync', + 'alpine:common', + 'alpine:docs', + 'alpine:linux', +] # Ubuntu ubuntu = ['ubuntu:swarm', 'ubuntu:android', 'ubuntu:vanilla'] @@ -25,14 +31,22 @@ for image in images: dir = image.replace(':', '-') print('Building %s in directory %s' % (image, dir)) - proc_args = ['docker', 'build', '-t', - 'dev-docker.governikus.de/ausweisapp2/%s' % (image), dir] + proc_args = [ + 'docker', + 'build', + '-t', + 'dev-docker.governikus.de/ausweisapp2/%s' % (image), + dir, + ] subprocess.run(proc_args).check_returncode() if 'push' in args: for image in images: print('Push %s' % image) - proc_args = ['docker', 'push', - 'dev-docker.governikus.de/ausweisapp2/%s' % (image)] + proc_args = [ + 'docker', + 'push', + 'dev-docker.governikus.de/ausweisapp2/%s' % (image), + ] subprocess.run(proc_args).check_returncode() diff -Nru ausweisapp2-2.3.1/resources/jenkins/docker/ubuntu-android/Dockerfile ausweisapp2-2.4.0/resources/jenkins/docker/ubuntu-android/Dockerfile --- ausweisapp2-2.3.1/resources/jenkins/docker/ubuntu-android/Dockerfile 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/docker/ubuntu-android/Dockerfile 2025-10-30 10:10:48.000000000 +0000 @@ -1,16 +1,16 @@ FROM dev-docker.governikus.de/ausweisapp2/ubuntu:swarm ARG ANDROID_CMDLINE_TOOLS=11076708 -ARG ANDROID_NDK_VERSION=27.2.12479018 -ARG CMAKE=3.30.6 -ARG MAVEN=3.9.9 +ARG ANDROID_NDK_VERSION=28.2.13676358 +ARG CMAKE=4.1.1 +ARG MAVEN=3.9.11 ENV NAME=Android LABELS="Android" PACKAGES_DIR=/home/governikus/packages ENV ANDROID_SDK_ROOT=/opt/android-sdk ENV ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION RUN apt-get update && \ - apt-get -y install g++ make ccache ninja-build perl unzip gradle patch openjdk-17-jdk-headless && \ + apt-get -y install g++ make ccache ninja-build perl unzip gradle patch openjdk-17-jdk-headless curl && \ rm -rf /var/lib/apt/lists/* RUN wget https://github.com/Kitware/CMake/releases/download/v$CMAKE/cmake-$CMAKE-Linux-x86_64.sh -O /tmp/cmake.sh && \ @@ -21,7 +21,7 @@ RUN mkdir -p /tmp/dl && cd /tmp/dl && wget -O sdk.zip https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_CMDLINE_TOOLS}_latest.zip && \ unzip sdk.zip && \ - yes | /tmp/dl/cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT "cmdline-tools;17.0" "build-tools;35.0.1" "platforms;android-35" "ndk;${ANDROID_NDK_VERSION}" && \ + yes | /tmp/dl/cmdline-tools/bin/sdkmanager --sdk_root=$ANDROID_SDK_ROOT "cmdline-tools;19.0" "build-tools;36.1.0" "platforms;android-35" "ndk;${ANDROID_NDK_VERSION}" && \ rm -rf /tmp/dl USER governikus diff -Nru ausweisapp2-2.3.1/resources/jenkins/docker/ubuntu-swarm/Dockerfile ausweisapp2-2.4.0/resources/jenkins/docker/ubuntu-swarm/Dockerfile --- ausweisapp2-2.3.1/resources/jenkins/docker/ubuntu-swarm/Dockerfile 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/docker/ubuntu-swarm/Dockerfile 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -FROM ubuntu:24.04 +FROM ubuntu:25.04 ARG JENKINS_SWARM_VERSION=3.48 ENV EXECUTOR=3 LABELS= NAME= PASSWORD= @@ -11,9 +11,15 @@ RUN useradd -m governikus -g users -u 1111 RUN apt-get update && \ - apt-get -y install openjdk-17-jre-headless tini python3-pip wget && \ + apt-get -y install openjdk-17-jre-headless tini python3-pip wget locales && \ pip3 install --break-system-packages rbtools mercurial python-hglib && \ rm -rf /var/lib/apt/lists/* RUN wget -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 +RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \ + locale-gen +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 + ADD swarm.sh / diff -Nru ausweisapp2-2.3.1/resources/jenkins/docker/ubuntu-vanilla/Dockerfile ausweisapp2-2.4.0/resources/jenkins/docker/ubuntu-vanilla/Dockerfile --- ausweisapp2-2.3.1/resources/jenkins/docker/ubuntu-vanilla/Dockerfile 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/docker/ubuntu-vanilla/Dockerfile 2025-10-30 10:10:48.000000000 +0000 @@ -6,7 +6,6 @@ apt-get -y install cmake make g++ clazy clang clang-tidy ccache gcovr cloc pkg-config ninja-build binutils-gold lld \ valgrind tree libpcsclite-dev libhttp-parser-dev libssl-dev libudev-dev \ libqt6opengl6-dev \ - libqt6shadertools6-dev \ libqt6svg6-dev \ libqt6websockets6-dev \ qt6-base-dev \ diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Android.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Android.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Android.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Android.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -2,7 +2,7 @@ import common.Constants // ----------------------------------------------------------------- APK -for(ARCH in Constants.AndroidArchAPK) +for(ARCH in Constants.AndroidArch) { def j = new Build @@ -54,7 +54,7 @@ // ----------------------------------------------------------------- AAR -for(ARCH in Constants.AndroidArchAAR) +for(ARCH in Constants.AndroidArch) { def j = new Build @@ -112,7 +112,7 @@ { parameters { - for(ARCH in Constants.AndroidArchAAR) + for(ARCH in Constants.AndroidArch) { stringParam(getNameParam(ARCH), '', 'Build of ' + ARCH) } @@ -120,7 +120,7 @@ steps { - for(ARCH in Constants.AndroidArchAAR) + for(ARCH in Constants.AndroidArch) { copyArtifacts(build.getSourceJobName('Android_AAR_' + ARCH)) { diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Appcast.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Appcast.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Appcast.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Appcast.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -import common.Appcast - -def j = new Appcast().generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_SDK.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_SDK.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_SDK.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_SDK.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -21,14 +21,14 @@ { steps { phase('Build') { - for(ARCH in Constants.AndroidArchAAR) + for(ARCH in Constants.AndroidArch) { phaseJob(getName('Android_AAR_' + ARCH)) } - phaseJob(getName('iOS_Framework')) + phaseJob(getName('iOS_Framework_OS')) - phaseJob(getName('iOS_Framework_Simulator')) + phaseJob(getName('iOS_Framework_Simulator_x86_64')) phaseJob(getName('iOS_Framework_Simulator_arm64')) } @@ -37,7 +37,7 @@ { parameters { - for(ARCH in Constants.AndroidArchAAR) + for(ARCH in Constants.AndroidArch) { predefinedProp('Android_AAR_' + ARCH.replace('-', '_'), getEnvNumber(getName('Android_AAR_' + ARCH))) } @@ -48,8 +48,8 @@ { parameters { - predefinedProp('iOS_Framework_Build', getEnvNumber(getName('iOS_Framework'))) - predefinedProp('iOS_Framework_Simulator_Build', getEnvNumber(getName('iOS_Framework_Simulator'))) + predefinedProp('iOS_Framework_OS_Build', getEnvNumber(getName('iOS_Framework_OS'))) + predefinedProp('iOS_Framework_Simulator_x86_64_Build', getEnvNumber(getName('iOS_Framework_Simulator_x86_64'))) predefinedProp('iOS_Framework_Simulator_arm64_Build', getEnvNumber(getName('iOS_Framework_Simulator_arm64'))) } } diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_SonarQube.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_SonarQube.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_SonarQube.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_SonarQube.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -5,8 +5,9 @@ ( name: 'SonarQube', label: 'Vanilla', - excludePattern: 'source/**,cache/**', + excludePattern: 'source/**,sonar/**', artifacts: 'tmp/sonar-metadata.txt', + trigger: '', xunit: true ).generate(this) @@ -18,7 +19,7 @@ environmentVariables { env("XDG_RUNTIME_DIR", '$WORKSPACE/tmp') - env("SONAR_USER_HOME", '$WORKSPACE/cache/sonar') + env("SONAR_USER_HOME", '$WORKSPACE/sonar/home') } } diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Win64_GNU_MSI.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Win64_GNU_MSI.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Win64_GNU_MSI.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Win64_GNU_MSI.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,7 @@ name: 'Win64_GNU_MSI', libraries: 'Win64_GNU', label: 'Windows', - artifacts: 'build/*.msi*', + artifacts: 'build/*.msi*,build/Appcast*', weight: 2 ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Win64_MSVC_MSI.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Win64_MSVC_MSI.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Win64_MSVC_MSI.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Win64_MSVC_MSI.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,7 @@ name: 'Win64_MSVC_MSI', libraries: 'Win64_MSVC', label: 'MSVC', - artifacts: 'build/*.msi*' + artifacts: 'build/*.msi*,build/Appcast*' ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Win64_MSVC_MSI_dev.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Win64_MSVC_MSI_dev.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Win64_MSVC_MSI_dev.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Win64_MSVC_MSI_dev.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,7 @@ name: 'Win64_MSVC_MSI_dev', libraries: 'Win64_MSVC_dev', label: 'MSVC', - artifacts: 'build/*.msi*' + artifacts: 'build/*.msi*,build/Appcast*' ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Win64_clang_MSI.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Win64_clang_MSI.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_Win64_clang_MSI.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_Win64_clang_MSI.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -6,7 +6,7 @@ name: 'Win64_clang_MSI', libraries: 'Win64_MSVC', label: 'MSVC', - artifacts: 'build/*.msi' + artifacts: 'build/*.msi,build/Appcast*' ).generate(this) @@ -25,7 +25,7 @@ name: 'Win64_clang++_MSI', libraries: 'Win64_MSVC', label: 'MSVC', - artifacts: 'build/*.msi' + artifacts: 'build/*.msi,build/Appcast*' ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_Framework.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_Framework.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_Framework.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_Framework.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -import common.Build - -def j = new Build - ( - name: 'iOS_Framework', - libraries: 'iOS', - label: 'iOS', - artifacts: 'build/*.zip,build/*.bcsymbolmap', - trigger: '' - ).generate(this) - - -j.with -{ - steps - { - shell('cmake -P source/ci.cmake') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_Framework_OS.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_Framework_OS.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_Framework_OS.groovy 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_Framework_OS.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,19 @@ +import common.Build + +def j = new Build + ( + name: 'iOS_Framework_OS', + libraries: 'iOS_OS', + label: 'iOS', + artifacts: 'build/*.zip,build/*.bcsymbolmap', + trigger: '' + ).generate(this) + + +j.with +{ + steps + { + shell('cmake -P source/ci.cmake') + } +} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_Framework_Simulator.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_Framework_Simulator.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_Framework_Simulator.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_Framework_Simulator.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -import common.Build - -def j = new Build - ( - name: 'iOS_Framework_Simulator', - libraries: 'iOS_Simulator', - label: 'iOS', - artifacts: 'build/*.zip,build/*.bcsymbolmap', - trigger: '' - ).generate(this) - - -j.with -{ - steps - { - shell('cmake -P source/ci.cmake') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_Framework_Simulator_x86_64.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_Framework_Simulator_x86_64.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_Framework_Simulator_x86_64.groovy 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_Framework_Simulator_x86_64.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,19 @@ +import common.Build + +def j = new Build + ( + name: 'iOS_Framework_Simulator_x86_64', + libraries: 'iOS_Simulator_x86_64', + label: 'iOS', + artifacts: 'build/*.zip,build/*.bcsymbolmap', + trigger: '' + ).generate(this) + + +j.with +{ + steps + { + shell('cmake -P source/ci.cmake') + } +} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_IPA.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -3,7 +3,7 @@ def j = new Build ( name: 'iOS_IPA', - libraries: 'iOS', + libraries: 'iOS_OS', label: 'iOS', artifacts: 'build/*.ipa,build/*.zip,build/*.bcsymbolmap' ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_SwiftPackage.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_SwiftPackage.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Builds/Build_iOS_SwiftPackage.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Builds/Build_iOS_SwiftPackage.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -15,30 +15,30 @@ { parameters { - stringParam('iOS_Framework_Build', '', 'Build of iOS Framework') - stringParam('iOS_Framework_Simulator_Build', '', 'Build of iOS Framework for Simulator') + stringParam('iOS_Framework_Build_OS', '', 'Build of iOS Framework for OS') + stringParam('iOS_Framework_Simulator_x86_64_Build', '', 'Build of iOS Framework for Simulator-x86_64') stringParam('iOS_Framework_Simulator_arm64_Build', '', 'Build of iOS Framework for Simulator-arm64') } steps { - copyArtifacts(build.getSourceJobName('iOS_Framework')) + copyArtifacts(build.getSourceJobName('iOS_Framework_OS')) { targetDirectory('arm64') flatten() buildSelector { - buildNumber('${iOS_Framework_Build}') + buildNumber('${iOS_Framework_OS_Build}') } } - copyArtifacts(build.getSourceJobName('iOS_Framework_Simulator')) + copyArtifacts(build.getSourceJobName('iOS_Framework_Simulator_x86_64')) { targetDirectory('x86_64-simulator') flatten() buildSelector { - buildNumber('${iOS_Framework_Simulator_Build}') + buildNumber('${iOS_Framework_Simulator_x86_64_Build}') } } diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Libraries/Libs_Android.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Libraries/Libs_Android.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Libraries/Libs_Android.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Libraries/Libs_Android.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -1,7 +1,7 @@ import common.Library import common.Constants -for(ARCH in Constants.AndroidArchLibs) +for(ARCH in Constants.AndroidArch) { def j = new Library diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Libraries/Libs_iOS.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Libraries/Libs_iOS.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Libraries/Libs_iOS.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Libraries/Libs_iOS.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -import common.Library - -def j = new Library - ( - name: 'iOS', - label: 'iOS' - ).generate(this) - - -j.with -{ - steps - { - shell('security unlock-keychain \${KEYCHAIN_CREDENTIALS} \${HOME}/Library/Keychains/login.keychain-db') - - shell('cd source/libs; cmake --preset ci-ios') - - shell('cmake --build build --target compress') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Libraries/Libs_iOS_OS.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Libraries/Libs_iOS_OS.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Libraries/Libs_iOS_OS.groovy 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Libraries/Libs_iOS_OS.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,20 @@ +import common.Library + +def j = new Library + ( + name: 'iOS_OS', + label: 'iOS' + ).generate(this) + + +j.with +{ + steps + { + shell('security unlock-keychain \${KEYCHAIN_CREDENTIALS} \${HOME}/Library/Keychains/login.keychain-db') + + shell('cd source/libs; cmake --preset ci-ios') + + shell('cmake --build build --target compress') + } +} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Libraries/Libs_iOS_Simulator.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Libraries/Libs_iOS_Simulator.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Libraries/Libs_iOS_Simulator.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Libraries/Libs_iOS_Simulator.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -import common.Library - -def j = new Library - ( - name: 'iOS_Simulator', - label: 'iOS' - ).generate(this) - - -j.with -{ - steps - { - shell('security unlock-keychain \${KEYCHAIN_CREDENTIALS} \${HOME}/Library/Keychains/login.keychain-db') - - shell('cd source/libs; cmake --preset ci-ios-simulator') - - shell('cmake --build build --target compress') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Libraries/Libs_iOS_Simulator_x86_64.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Libraries/Libs_iOS_Simulator_x86_64.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Libraries/Libs_iOS_Simulator_x86_64.groovy 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Libraries/Libs_iOS_Simulator_x86_64.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,20 @@ +import common.Library + +def j = new Library + ( + name: 'iOS_Simulator_x86_64', + label: 'iOS' + ).generate(this) + + +j.with +{ + steps + { + shell('security unlock-keychain \${KEYCHAIN_CREDENTIALS} \${HOME}/Library/Keychains/login.keychain-db') + + shell('cd source/libs; cmake --preset ci-ios-simulator-x86_64') + + shell('cmake --build build --target compress') + } +} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_Android.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_Android.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_Android.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_Android.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -4,7 +4,7 @@ // ----------------------------------------------------------------- APK -for(ARCH in Constants.AndroidArchAPK) +for(ARCH in Constants.AndroidArch) { def j = new Release @@ -51,7 +51,7 @@ // ----------------------------------------------------------------- AAR -for(ARCH in Constants.AndroidArchAAR) +for(ARCH in Constants.AndroidArch) { def j = new Release @@ -97,9 +97,7 @@ { parameters { - booleanParam("PUBLISH", false, "Publish to maven central") - - for(ARCH in Constants.AndroidArchAAR) + for(ARCH in Constants.AndroidArch) { buildSelectorParam(build.getSourceJobNameParam('Android_AAR_' + ARCH)) { @@ -114,7 +112,7 @@ steps { - for(ARCH in Constants.AndroidArchAAR) + for(ARCH in Constants.AndroidArch) { copyArtifacts(build.getSourceJobName('Android_AAR_' + ARCH)) { diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_Appcast.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_Appcast.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_Appcast.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_Appcast.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -import common.Appcast - -def j = new Appcast - ( - namePrefix: 'Release_', - releaseJob: true - ).generate(this) - - -j.with -{ - parameters - { - stringParam('changeset', 'release', 'Build given changeset (tag) as release') - } - - wrappers - { - buildName('${changeset}') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_Container.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_Container.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_Container.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_Container.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -11,11 +11,6 @@ j.with { - parameters - { - booleanParam("LATEST", false, "Use latest tag") - } - steps { shell('cmake -P source/ci.cmake') diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_Win64_GNU.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_Win64_GNU.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_Win64_GNU.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_Win64_GNU.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,7 @@ name: 'Win64_GNU_MSI', libraries: 'Win64_GNU', label: 'Windows', - artifacts: 'libs/Toolchain_*,build/*.msi*', + artifacts: 'libs/Toolchain_*,build/*.msi*,build/Appcast*', weight: 2 ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_Win64_MSVC.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_Win64_MSVC.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_Win64_MSVC.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_Win64_MSVC.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,7 @@ name: 'Win64_MSVC_MSI', libraries: 'Win64_MSVC', label: 'MSVC', - artifacts: 'libs/Toolchain_*,build/*.msi*' + artifacts: 'libs/Toolchain_*,build/*.msi*,build/Appcast*' ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -3,7 +3,7 @@ def j = new Release ( name: 'iOS_IPA', - libraries: 'iOS', + libraries: 'iOS_OS', label: 'iOS', artifacts: 'libs/Toolchain_*,build/*.ipa,build/*.zip,build/*.bcsymbolmap,*.tar.zstd' ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS_Framework.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS_Framework.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS_Framework.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS_Framework.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -import common.Release - -def j = new Release - ( - name: 'iOS_Framework', - libraries: 'iOS', - label: 'iOS', - artifacts: 'libs/Toolchain_*,build/*.zip,build/*.bcsymbolmap,*.tar.zstd' - ).generate(this) - - -j.with -{ - steps - { - shell('cmake -P source/ci.cmake') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS_Framework_OS.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS_Framework_OS.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS_Framework_OS.groovy 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS_Framework_OS.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,18 @@ +import common.Release + +def j = new Release + ( + name: 'iOS_Framework_OS', + libraries: 'iOS_OS', + label: 'iOS', + artifacts: 'libs/Toolchain_*,build/*.zip,build/*.bcsymbolmap,*.tar.zstd' + ).generate(this) + + +j.with +{ + steps + { + shell('cmake -P source/ci.cmake') + } +} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS_Framework_Simulator.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS_Framework_Simulator.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS_Framework_Simulator.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS_Framework_Simulator.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -import common.Release - -def j = new Release - ( - name: 'iOS_Framework_Simulator', - libraries: 'iOS_Simulator', - label: 'iOS', - artifacts: 'libs/Toolchain_*,build/*.zip,build/*.bcsymbolmap,*.tar.zstd' - ).generate(this) - - -j.with -{ - steps - { - shell('cmake -P source/ci.cmake') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS_Framework_Simulator_x86_64.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS_Framework_Simulator_x86_64.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS_Framework_Simulator_x86_64.groovy 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS_Framework_Simulator_x86_64.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,18 @@ +import common.Release + +def j = new Release + ( + name: 'iOS_Framework_Simulator_x86_64', + libraries: 'iOS_Simulator_x86_64', + label: 'iOS', + artifacts: 'libs/Toolchain_*,build/*.zip,build/*.bcsymbolmap,*.tar.zstd' + ).generate(this) + + +j.with +{ + steps + { + shell('cmake -P source/ci.cmake') + } +} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS_SwiftPackage.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS_SwiftPackage.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Releases/Release_iOS_SwiftPackage.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Releases/Release_iOS_SwiftPackage.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -14,22 +14,22 @@ { parameters { - buildSelectorParam('iOS_Framework_Build') + buildSelectorParam('iOS_Framework_OS_Build') { defaultBuildSelector { latestSuccessful(true) } - description('Build to get iOS Framework artifacts from') + description('Build to get iOS Framework OS artifacts from') } - buildSelectorParam('iOS_Framework_Simulator_Build') + buildSelectorParam('iOS_Framework_Simulator_x86_64_Build') { defaultBuildSelector { latestSuccessful(true) } - description('Build to get iOS Framework (Simulator) artifacts from') + description('Build to get iOS Framework (Simulator-x86_64) artifacts from') } buildSelectorParam('iOS_Framework_Simulator_arm64_Build') { @@ -43,23 +43,23 @@ steps { - copyArtifacts(build.getSourceJobName('iOS_Framework')) + copyArtifacts(build.getSourceJobName('iOS_Framework_OS')) { targetDirectory('arm64') flatten() buildSelector { - buildParameter('iOS_Framework_Build') + buildParameter('iOS_Framework_OS_Build') } } - copyArtifacts(build.getSourceJobName('iOS_Framework_Simulator')) + copyArtifacts(build.getSourceJobName('iOS_Framework_Simulator_x86_64')) { targetDirectory('x86_64-simulator') flatten() buildSelector { - buildParameter('iOS_Framework_Simulator_Build') + buildParameter('iOS_Framework_Simulator_x86_64_Build') } } diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Android.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Android.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Android.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Android.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -2,7 +2,7 @@ import common.Constants // ----------------------------------------------------------------- APK -for(ARCH in Constants.AndroidArchAPKReview) +for(ARCH in Constants.AndroidArch) { def j = new Review @@ -43,7 +43,7 @@ // ----------------------------------------------------------------- AAR -for(ARCH in Constants.AndroidArchAARReview) +for(ARCH in Constants.AndroidArch) { def j = new Review @@ -95,7 +95,7 @@ { parameters { - for(ARCH in Constants.AndroidArchAARReview) + for(ARCH in Constants.AndroidArch) { stringParam(build.getSourceJobNameParam('Android_AAR_' + ARCH), '', 'Build of ' + ARCH) } @@ -103,7 +103,7 @@ steps { - for(ARCH in Constants.AndroidArchAARReview) + for(ARCH in Constants.AndroidArch) { copyArtifacts(build.getSourceJobName('Android_AAR_' + ARCH)) { diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Formatting.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Formatting.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Formatting.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Formatting.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -4,7 +4,7 @@ ( name: 'Formatting', libraries: 'Linux', - label: 'Common' + label: 'Linux' ).generate(this) @@ -14,7 +14,7 @@ steps { - shell('cmake -DPENDING=OFF -P source/ci.cmake') + shell('cmake -P source/ci.cmake') } publishers diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Libs_Android.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Libs_Android.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Libs_Android.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Libs_Android.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -1,7 +1,7 @@ import common.LibraryReview import common.Constants -for(ARCH in Constants.AndroidArchLibsReview) +for(ARCH in Constants.AndroidArch) { def j = new LibraryReview diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Libs_iOS.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Libs_iOS.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Libs_iOS.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Libs_iOS.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -import common.LibraryReview - -def j = new LibraryReview - ( - name: 'iOS', - label: 'iOS' - ).generate(this) - - -j.with -{ - steps - { - shell('cmake -DPATCH_ONLY=ON -P source/ci.cmake') - - shell('security unlock-keychain \${KEYCHAIN_CREDENTIALS} \${HOME}/Library/Keychains/login.keychain-db') - - shell('cd source/libs; cmake --preset ci-ios') - - shell('cmake --build build --target compress') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Libs_iOS_OS.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Libs_iOS_OS.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Libs_iOS_OS.groovy 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Libs_iOS_OS.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,22 @@ +import common.LibraryReview + +def j = new LibraryReview + ( + name: 'iOS_OS', + label: 'iOS' + ).generate(this) + + +j.with +{ + steps + { + shell('cmake -DPATCH_ONLY=ON -P source/ci.cmake') + + shell('security unlock-keychain \${KEYCHAIN_CREDENTIALS} \${HOME}/Library/Keychains/login.keychain-db') + + shell('cd source/libs; cmake --preset ci-ios') + + shell('cmake --build build --target compress') + } +} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Libs_iOS_Simulator.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Libs_iOS_Simulator.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Libs_iOS_Simulator.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Libs_iOS_Simulator.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -import common.LibraryReview - -def j = new LibraryReview - ( - name: 'iOS_Simulator', - label: 'iOS' - ).generate(this) - - -j.with -{ - steps - { - shell('cmake -DPATCH_ONLY=ON -P source/ci.cmake') - - shell('security unlock-keychain \${KEYCHAIN_CREDENTIALS} \${HOME}/Library/Keychains/login.keychain-db') - - shell('cd source/libs; cmake --preset ci-ios-simulator') - - shell('cmake --build build --target compress') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Libs_iOS_Simulator_x86_64.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Libs_iOS_Simulator_x86_64.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Libs_iOS_Simulator_x86_64.groovy 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Libs_iOS_Simulator_x86_64.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,22 @@ +import common.LibraryReview + +def j = new LibraryReview + ( + name: 'iOS_Simulator_x86_64', + label: 'iOS' + ).generate(this) + + +j.with +{ + steps + { + shell('cmake -DPATCH_ONLY=ON -P source/ci.cmake') + + shell('security unlock-keychain \${KEYCHAIN_CREDENTIALS} \${HOME}/Library/Keychains/login.keychain-db') + + shell('cd source/libs; cmake --preset ci-ios-simulator-x86_64') + + shell('cmake --build build --target compress') + } +} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_SonarQube.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_SonarQube.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_SonarQube.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_SonarQube.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -6,7 +6,7 @@ name: 'SonarQube', label: 'Vanilla', artifacts: 'tmp/*.log,tmp/sonar-metadata.txt', - excludePattern: 'source/**,cache/**', + excludePattern: 'source/**,sonar/**', allowEmptyArtifacts: true, xunit: true ).generate(this) @@ -19,12 +19,12 @@ environmentVariables { env("XDG_RUNTIME_DIR", '$WORKSPACE/tmp') - env("SONAR_USER_HOME", '$WORKSPACE/cache/sonar') + env("SONAR_USER_HOME", '$WORKSPACE/sonar/home') } } steps { - shell('cmake -DSPLITTED=OFF -P source/ci.cmake') + shell('cmake -DSPLIT=OFF -P source/ci.cmake') } } diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Trigger.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Trigger.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Trigger.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Trigger.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -8,12 +8,12 @@ { def list = ['Formatting', 'Source', 'Docs'] - def packages = ['Container', 'MacOS_DMG_PKG', 'Win64_GNU_MSI', 'Win64_MSVC_MSI', 'iOS_IPA', 'iOS_Framework', 'iOS_Framework_Simulator', 'iOS_Framework_Simulator_arm64'] - for(ARCH in Constants.AndroidArchAARReview) + def packages = ['Container', 'MacOS_DMG_PKG', 'Win64_GNU_MSI', 'Win64_MSVC_MSI', 'iOS_IPA', 'iOS_Framework_OS', 'iOS_Framework_Simulator_x86_64', 'iOS_Framework_Simulator_arm64'] + for(ARCH in Constants.AndroidArch) { packages << 'Android_AAR_' + ARCH } - for(ARCH in Constants.AndroidArchAPKReview) + for(ARCH in Constants.AndroidArch) { packages << 'Android_APK_' + ARCH } @@ -78,12 +78,12 @@ phase('Packages', 'UNSTABLE') { - for(ARCH in Constants.AndroidArchAARReview) + for(ARCH in Constants.AndroidArch) { phaseJob(getName('Android_AAR_' + ARCH)) } - for(ARCH in Constants.AndroidArchAPKReview) + for(ARCH in Constants.AndroidArch) { phaseJob(getName('Android_APK_' + ARCH)) } @@ -96,9 +96,9 @@ phaseJob(getName('iOS_IPA')) - phaseJob(getName('iOS_Framework')) + phaseJob(getName('iOS_Framework_OS')) - phaseJob(getName('iOS_Framework_Simulator')) + phaseJob(getName('iOS_Framework_Simulator_x86_64')) phaseJob(getName('iOS_Framework_Simulator_arm64')) @@ -111,7 +111,7 @@ { parameters { - for(ARCH in Constants.AndroidArchAARReview) + for(ARCH in Constants.AndroidArch) { predefinedProp(getNameParam('Android_AAR_' + ARCH), getEnvNumber(getName('Android_AAR_' + ARCH))) } @@ -122,8 +122,8 @@ { parameters { - predefinedProp('iOS_Framework_Build', getEnvNumber(getName('iOS_Framework'))) - predefinedProp('iOS_Framework_Simulator_Build', getEnvNumber(getName('iOS_Framework_Simulator'))) + predefinedProp('iOS_Framework_OS_Build', getEnvNumber(getName('iOS_Framework_OS'))) + predefinedProp('iOS_Framework_Simulator_x86_64_Build', getEnvNumber(getName('iOS_Framework_Simulator_x86_64'))) predefinedProp('iOS_Framework_Simulator_arm64_Build', getEnvNumber(getName('iOS_Framework_Simulator_arm64'))) } } diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Trigger_Libs.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Trigger_Libs.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Trigger_Libs.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Trigger_Libs.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -5,8 +5,8 @@ def getJobs() { - def list = ['Linux', 'MacOS', 'Win64_GNU', 'Win64_MSVC', 'Win64_MSVC_dev', 'iOS', 'iOS_Simulator', 'iOS_Simulator_arm64', 'FreeBSD'] - for(ARCH in Constants.AndroidArchLibsReview) + def list = ['Linux', 'MacOS', 'Win64_GNU', 'Win64_MSVC', 'Win64_MSVC_dev', 'iOS_OS', 'iOS_Simulator_x86_64', 'iOS_Simulator_arm64', 'FreeBSD'] + for(ARCH in Constants.AndroidArch) { list << 'Android_' + ARCH } @@ -54,7 +54,7 @@ killPhaseCondition('NEVER') } - for(ARCH in Constants.AndroidArchLibsReview) + for(ARCH in Constants.AndroidArch) { phaseJob(getName('Android_' + ARCH)) { @@ -86,13 +86,13 @@ killPhaseCondition('NEVER') } - phaseJob(getName('iOS')) + phaseJob(getName('iOS_OS')) { abortAllJobs(false) killPhaseCondition('NEVER') } - phaseJob(getName('iOS_Simulator')) + phaseJob(getName('iOS_Simulator_x86_64')) { abortAllJobs(false) killPhaseCondition('NEVER') diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Win64_GNU_MSI.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Win64_GNU_MSI.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Win64_GNU_MSI.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Win64_GNU_MSI.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,7 @@ name: 'Win64_GNU_MSI', libraries: 'Win64_GNU', label: 'Windows', - artifacts: 'build/*.msi*', + artifacts: 'build/*.msi*,build/Appcast*', weight: 2 ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Win64_MSVC_MSI.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Win64_MSVC_MSI.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_Win64_MSVC_MSI.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_Win64_MSVC_MSI.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,7 @@ name: 'Win64_MSVC_MSI', libraries: 'Win64_MSVC', label: 'MSVC', - artifacts: 'build/*.msi*' + artifacts: 'build/*.msi*,build/Appcast*' ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_Framework.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_Framework.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_Framework.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_Framework.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -import common.Review - -def j = new Review - ( - name: 'iOS_Framework', - libraries: 'iOS', - label: 'iOS', - artifacts: 'build/*.zip,build/*.bcsymbolmap' - ).generate(this) - - -j.with -{ - steps - { - shell('cmake -P source/ci.cmake') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_Framework_OS.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_Framework_OS.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_Framework_OS.groovy 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_Framework_OS.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,18 @@ +import common.Review + +def j = new Review + ( + name: 'iOS_Framework_OS', + libraries: 'iOS_OS', + label: 'iOS', + artifacts: 'build/*.zip,build/*.bcsymbolmap' + ).generate(this) + + +j.with +{ + steps + { + shell('cmake -P source/ci.cmake') + } +} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_Framework_Simulator.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_Framework_Simulator.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_Framework_Simulator.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_Framework_Simulator.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -import common.Review - -def j = new Review - ( - name: 'iOS_Framework_Simulator', - libraries: 'iOS_Simulator', - label: 'iOS', - artifacts: 'build/*.zip,build/*.bcsymbolmap' - ).generate(this) - - -j.with -{ - steps - { - shell('cmake -P source/ci.cmake') - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_Framework_Simulator_x86_64.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_Framework_Simulator_x86_64.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_Framework_Simulator_x86_64.groovy 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_Framework_Simulator_x86_64.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,18 @@ +import common.Review + +def j = new Review + ( + name: 'iOS_Framework_Simulator_x86_64', + libraries: 'iOS_Simulator_x86_64', + label: 'iOS', + artifacts: 'build/*.zip,build/*.bcsymbolmap' + ).generate(this) + + +j.with +{ + steps + { + shell('cmake -P source/ci.cmake') + } +} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_IPA.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -3,7 +3,7 @@ def j = new Review ( name: 'iOS_IPA', - libraries: 'iOS', + libraries: 'iOS_OS', label: 'iOS', artifacts: 'build/*.ipa,build/*.zip,build/*.bcsymbolmap' ).generate(this) diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_SwiftPackage.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_SwiftPackage.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/Reviews/Review_iOS_SwiftPackage.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/Reviews/Review_iOS_SwiftPackage.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -14,30 +14,30 @@ { parameters { - stringParam('iOS_Framework_Build', '', 'Build of iOS Framework') - stringParam('iOS_Framework_Simulator_Build', '', 'Build of iOS Framework for Simulator') + stringParam('iOS_Framework_OS_Build', '', 'Build of iOS Framework for OS') + stringParam('iOS_Framework_Simulator_x86_64_Build', '', 'Build of iOS Framework for Simulator-x86_64') stringParam('iOS_Framework_Simulator_arm64_Build', '', 'Build of iOS Framework for Simulator-arm64') } steps { - copyArtifacts(build.getSourceJobName('iOS_Framework')) + copyArtifacts(build.getSourceJobName('iOS_Framework_OS')) { targetDirectory('arm64') flatten() buildSelector { - buildNumber('${iOS_Framework_Build}') + buildNumber('${iOS_Framework_OS_Build}') } } - copyArtifacts(build.getSourceJobName('iOS_Framework_Simulator')) + copyArtifacts(build.getSourceJobName('iOS_Framework_Simulator_x86_64')) { targetDirectory('x86_64-simulator') flatten() buildSelector { - buildNumber('${iOS_Framework_Simulator_Build}') + buildNumber('${iOS_Framework_Simulator_x86_64_Build}') } } diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/common/Appcast.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/common/Appcast.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/common/Appcast.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/common/Appcast.groovy 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -package common - -import javaposse.jobdsl.dsl.Job -import javaposse.jobdsl.dsl.DslFactory - -import common.Build - -class Appcast extends Build -{ - String name = 'Appcast' - String label= 'Common' - String artifacts = 'build/*.msi,build/*.dmg,build/*.tar.gz,build/*.sha256,build/Appcast*,build/ReleaseNotes.html,build/*.pdf,build/docs/**/*.pdf,build/docs/**/*.tar.xz' - String trigger = null - List oldBuilds = [-1, 5] - boolean sendMail = false - - Job generate(DslFactory dslFactory) - { - def j = super.generate(dslFactory) - - j.with - { - if(!getReleaseJob()) - { - triggers - { - upstream(getSourceJobName('Win64_MSVC_MSI') + ',' + getSourceJobName('MacOS_DMG_PKG') + ',' + getSourceJobName('Source') + ',' + getSourceJobName('Docs')) - } - } - - steps - { - copyArtifacts(getSourceJobName('Win64_MSVC_MSI')) - { - buildSelector - { - latestSuccessful(true) - } - } - - copyArtifacts(getSourceJobName('MacOS_DMG_PKG')) - { - buildSelector - { - latestSuccessful(true) - } - } - - copyArtifacts(getSourceJobName('Source')) - { - buildSelector - { - latestSuccessful(true) - } - } - - - copyArtifacts(getSourceJobName('Docs')) - { - buildSelector - { - latestSuccessful(true) - } - } - - shell("cd source; cmake --preset ci-tools") - shell('cd build; cmake -E copy docs/notes/singlehtml/de/appcast.html ReleaseNotes.html') - } - } - - return j - } -} diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/common/Build.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/common/Build.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/common/Build.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/common/Build.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -263,7 +263,7 @@ } if(getSendMail()) - mailer('', true, true) + mailer('${DEFAULT_EMAIL_RECIPIENTS}', true, true) } } diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/common/Constants.groovy ausweisapp2-2.4.0/resources/jenkins/dsl/common/Constants.groovy --- ausweisapp2-2.3.1/resources/jenkins/dsl/common/Constants.groovy 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/common/Constants.groovy 2025-10-30 10:10:48.000000000 +0000 @@ -2,13 +2,7 @@ class Constants { - static final AndroidArchAPKReview = ["arm64-v8a", "x86_64"] - static final AndroidArchAPK = AndroidArchAPKReview + ["armeabi-v7a", "x86"] - static final AndroidArchAARReview = ["arm64-v8a", "x86_64", "armeabi-v7a"] - static final AndroidArchAAR = AndroidArchAARReview - - static final AndroidArchLibsReview = (AndroidArchAPKReview + AndroidArchAARReview).unique() - static final AndroidArchLibs = (AndroidArchAPK + AndroidArchAAR).unique() + static final AndroidArch = ["armeabi-v7a", "arm64-v8a", "x86_64"] static String strip(String content) { diff -Nru ausweisapp2-2.3.1/resources/jenkins/dsl/install.py ausweisapp2-2.4.0/resources/jenkins/dsl/install.py --- ausweisapp2-2.3.1/resources/jenkins/dsl/install.py 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/dsl/install.py 2025-10-30 10:10:48.000000000 +0000 @@ -3,8 +3,8 @@ import sys if sys.version_info < (3, 1): - print("Python 3.1 or greater is required") - sys.exit(1) + print('Python 3.1 or greater is required') + sys.exit(1) import os import tempfile @@ -14,48 +14,60 @@ import time - if len(sys.argv) != 2: - print('Please provide address of jenkins server') - print('Example: localhost:8090') - sys.exit(1) + print('Please provide address of jenkins server') + print('Example: localhost:8090') + sys.exit(1) if not shutil.which('java'): - print('Cannot find java') - sys.exit(1) + print('Cannot find java') + sys.exit(1) -filename = "jenkins-cli.jar" +filename = 'jenkins-cli.jar' jenkinsJar = tempfile.gettempdir() + '/' + filename if not os.path.isfile(jenkinsJar): - try: - url = 'http://' + sys.argv[1] + '/jnlpJars/' + filename - print('Download ' + url) - jenkinsJar, headers = urllib.request.urlretrieve(url, jenkinsJar) - except Exception as e: - print('Cannot download ' + filename + ': ' + str(e)) - sys.exit(1) + try: + url = 'http://' + sys.argv[1] + '/jnlpJars/' + filename + print('Download ' + url) + jenkinsJar, headers = urllib.request.urlretrieve(url, jenkinsJar) + except Exception as e: + print('Cannot download ' + filename + ': ' + str(e)) + sys.exit(1) else: - print('Use existing: ' + jenkinsJar) - + print('Use existing: ' + jenkinsJar) -def systemCall(cmd, content = None): - print('Calling: ' + cmd) - try: - result = str(subprocess.check_output(cmd, stdin=content, shell=True), 'UTF8').strip() - except subprocess.CalledProcessError as e: - print('Got error: ' + str(e.output, 'UTF8')) - return 'code: ' + str(e.returncode) - print('Got: ' + result) - return result +def systemCall(cmd, content=None): + print('Calling: ' + cmd) + try: + result = str( + subprocess.check_output(cmd, stdin=content, shell=True), 'UTF8' + ).strip() + except subprocess.CalledProcessError as e: + print('Got error: ' + str(e.output, 'UTF8')) + return 'code: ' + str(e.returncode) + + print('Got: ' + result) + return result + + +def callJenkins(cmd, content=None): + return systemCall( + 'java -jar ' + + jenkinsJar + + ' -noKeyAuth -s http://' + + sys.argv[1] + + ' ' + + cmd, + content, + ) -def callJenkins(cmd, content = None): - return systemCall('java -jar ' + jenkinsJar + ' -noKeyAuth -s http://' + sys.argv[1] + ' ' + cmd, content) def installPlugin(plugin): - return callJenkins('install-plugin ' + ' '.join(plugin) + ' -restart') + return callJenkins('install-plugin ' + ' '.join(plugin) + ' -restart') + initialSessionId = callJenkins('session-id') alreadyInstalledPlugins = callJenkins('list-plugins') @@ -98,29 +110,36 @@ installPlugins = [] for plugin in plugins: - if plugin not in alreadyInstalledPlugins: - print('Try to install plugin: ' + plugin) - installPlugins.append(plugin) + if plugin not in alreadyInstalledPlugins: + print('Try to install plugin: ' + plugin) + installPlugins.append(plugin) if installPlugins: - print('Try to install plugins... show progress at http://' + sys.argv[1] + '/updateCenter/') - if 'code: ' in installPlugin(installPlugins): - print('Cannot install plugins!') - print('Maybe you need to configure a proxy in your jenkins to get update server?') - sys.exit(1) - - print('Wait until jenkins is restarted...') - while 1: - time.sleep(5) - result = callJenkins('session-id') - if result != initialSessionId and 'code: ' not in result: - break - print('Wait...') - - print('Jenkins restarted...') - - -content = open('config.xml', "r") -if not 'code: ' in callJenkins('create-job _Seeder_', content): - callJenkins('build _Seeder_') + print( + 'Try to install plugins... show progress at http://' + + sys.argv[1] + + '/updateCenter/' + ) + if 'code: ' in installPlugin(installPlugins): + print('Cannot install plugins!') + print( + 'Maybe you need to configure a proxy ' + 'in your jenkins to get update server?' + ) + sys.exit(1) + + print('Wait until jenkins is restarted...') + while 1: + time.sleep(5) + result = callJenkins('session-id') + if result != initialSessionId and 'code: ' not in result: + break + print('Wait...') + + print('Jenkins restarted...') + + +content = open('config.xml', 'r') +if 'code: ' not in callJenkins('create-job _Seeder_', content): + callJenkins('build _Seeder_') diff -Nru ausweisapp2-2.3.1/resources/jenkins/import.py ausweisapp2-2.4.0/resources/jenkins/import.py --- ausweisapp2-2.3.1/resources/jenkins/import.py 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,173 +0,0 @@ -#!/usr/bin/env python - -import hglib -from hglib.util import b - -import argparse -import os -import glob -import sys - -from pathlib import Path - - -class CommandLineParser(argparse.ArgumentParser): - def __init__(self): - super().__init__() - self.add_argument('--no-pending', - help='Do not apply patch', action='store_true') - self.add_argument('--clean', - help='Clean up patches', action='store_true') - self.add_argument('--clean-only', - help='Clean up only', action='store_true') - self.add_argument('--splitted', - help='Apply patches splitted', action='store_true') - self.add_argument('--repatch', - help="Repatch previous series", action='store_true') - self.add_argument('--patch', - help="Patch file", default='../patch.diff') - self.args = self.parse_args() - - def pending(self): - return not self.args.no_pending - - def clean(self): - return self.args.clean or self.clean_only() - - def clean_only(self): - return self.args.clean_only - - def splitted(self): - return self.args.splitted - - def repatch(self): - return self.args.repatch - - def patch(self): - return self.args.patch - - -class PatchProcessor(): - def __init__(self, file, repatch): - self.file = file.decode('utf-8') - self.dir = os.path.dirname(os.path.realpath(self.file)) - self.patch_series = sorted(glob.glob(self.dir + os.sep + '*.patch'), - key=lambda x: int(Path(x).stem)) - self.applied = self.dir + os.sep + 'applied' - self.applied_patches = [] - - if os.path.isfile(self.applied): - with open(self.applied, 'r') as f: - self.applied_patches = list(filter(None, f.read().split('\n'))) - if repatch and len(self.applied_patches) > 0: - self.applied_patches.pop() - - def clean(self): - if os.path.isfile(self.applied): - os.remove(self.applied) - for patch in self.patch_series: - os.remove(patch) - - self.patch_series = [] - self.applied_patches = [] - - def split(self): - def nextFilename(): - self.patch_series.append(self.dir + os.sep + - str(len(self.patch_series)) + - '.patch') - return open(self.patch_series[-1], 'wb') - - with open(self.file, 'rb') as f: - file = nextFilename() - for line in f: - if line == b'# HG changeset patch\n' and file.tell() > 0: - file.close() - file = nextFilename() - file.write(line) - file.close() - - def isSplitted(self): - return len(self.patch_series) > 0 - - def apply(self, client, splitted, applyPending): - if self.patch_series == self.applied_patches or not splitted: - print('Apply all patches: %s' % len(self.patch_series)) - client.import_([s.encode('utf-8') for s in self.patch_series], - nocommit=True) - return - - with open(self.applied, 'w') as f: - for patch in self.patch_series: - pending = patch not in self.applied_patches - if not pending or applyPending and pending: - print('Apply patch: {} (pending: {})'.format(patch, - pending)) - client.import_([patch.encode('utf-8')], - user='CI', - date='now', - message=patch, - nocommit=pending) - - f.write(patch) - f.write('\n') - if pending: - print('Pending patch: {}'.format(patch)) - break - - -def main(): - parser = CommandLineParser() - - patch = b(parser.patch()) - if not os.path.isfile(patch): - if not os.path.isfile(patch): - print('Patch file "{}" does not exists'.format(patch)) - return -1 - - if 'BUILD_URL' not in os.environ: - print('Warning: This script should be called on jenkins only') - return -1 - - cfg = ['extensions.hgext.purge=', - 'extensions.hgext.strip=', - 'patch.eol=auto', - 'phases.new-commit=draft'] - client = hglib.open(configs=cfg) - - rev = client.identify(id=True).replace(b('+'), b('')).rstrip() - print('Revert workspace to: ' + str(rev)) - client.update(rev=rev, clean=True) - - print('Purge workspace...') - client.rawcommand([b('purge'), b('--all')]) - - revs = len(client.log(revrange='secret() or draft()')) - print('Found secret/draft changesets: {}'.format(revs)) - - if revs > 0: - print('Strip secret and draft changesets...') - client.rawcommand([b('strip'), - b('-r'), - b('secret() or draft()'), - b('--no-backup'), - b('--force')]) - - processor = PatchProcessor(patch, parser.repatch()) - if parser.clean(): - processor.clean() - - if parser.clean_only(): - return 0 - - if not processor.isSplitted(): - print('Split patch: {}'.format(patch)) - processor.split() - print('Wrote {} patch(es)'.format(len(processor.patch_series))) - - processor.apply(client, parser.splitted(), parser.pending()) - return 0 - - -if __name__ == '__main__': - sys.exit(main()) diff -Nru ausweisapp2-2.3.1/resources/jenkins/kubernetes/android.yaml ausweisapp2-2.4.0/resources/jenkins/kubernetes/android.yaml --- ausweisapp2-2.3.1/resources/jenkins/kubernetes/android.yaml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/kubernetes/android.yaml 2025-10-30 10:10:48.000000000 +0000 @@ -1,151 +1,188 @@ apiVersion: apps/v1 kind: Deployment metadata: + labels: + cattle.io/creator: norman + workload.user.cattle.io/workloadselector: deployment-ausweisapp-android name: android namespace: ausweisapp spec: + progressDeadlineSeconds: 600 replicas: 3 + revisionHistoryLimit: 10 selector: matchLabels: workload.user.cattle.io/workloadselector: deployment-ausweisapp-android strategy: type: Recreate template: + metadata: + labels: + workload.user.cattle.io/workloadselector: deployment-ausweisapp-android + namespace: ausweisapp spec: containers: - - env: - - name: EXECUTOR - value: "2" - - name: APK_SIGN_KEYSTORE - value: /home/governikus/release.keystore - - name: APK_SIGN_KEYSTORE_ALIAS - value: autentapp - - name: APK_SIGN_KEYSTORE_ALIAS_DEV - value: androiddebugkey - - name: APK_SIGN_KEYSTORE_DEV - value: /home/governikus/.android/debug.keystore - - name: APK_SIGN_KEYSTORE_PSW_DEV - value: android - - name: CCACHE_DIR - value: /ccache - - name: CMAKE_BUILD_PARALLEL_LEVEL - value: 5 - - name: CTEST_PARALLEL_LEVEL - value: 5 - - name: PROCESSOR_COUNT - value: 5 - - name: APK_SIGN_KEYSTORE_PSW - valueFrom: - secretKeyRef: - key: APK_SIGN_KEYSTORE_PSW - name: android-passwords - optional: false - - name: NEXUS_PSW - valueFrom: - secretKeyRef: - key: NEXUS_PSW - name: android-passwords - optional: false - - name: NEXUS_USERNAME - valueFrom: - secretKeyRef: - key: NEXUS_USERNAME - name: android-passwords - optional: false - - name: PASSWORD - valueFrom: - secretKeyRef: - key: PASSWORD - name: android-passwords - optional: false - - name: SONARQUBE_TOKEN - valueFrom: - secretKeyRef: - key: sonarqube - name: dependency-check-credentials - optional: false - - name: GPG_PSW - valueFrom: - secretKeyRef: - key: GPG_PSW + - env: + - name: APK_SIGN_KEYSTORE + value: /home/governikus/release.keystore + - name: APK_SIGN_KEYSTORE_ALIAS + value: autentapp + - name: APK_SIGN_KEYSTORE_ALIAS_DEV + value: androiddebugkey + - name: APK_SIGN_KEYSTORE_DEV + value: /home/governikus/.android/debug.keystore + - name: APK_SIGN_KEYSTORE_PSW_DEV + value: android + - name: CMAKE_BUILD_PARALLEL_LEVEL + value: '5' + - name: CTEST_PARALLEL_LEVEL + value: '5' + - name: EXECUTOR + value: '2' + - name: FINGERPRINTS + valueFrom: + secretKeyRef: + key: FINGERPRINT + name: generic-secrets + optional: false + - name: APK_SIGN_KEYSTORE_PSW + valueFrom: + secretKeyRef: + key: APK_SIGN_KEYSTORE_PSW + name: android-passwords + optional: false + - name: NEXUS_PSW + valueFrom: + secretKeyRef: + key: NEXUS_PSW + name: android-passwords + optional: false + - name: NEXUS_USERNAME + valueFrom: + secretKeyRef: + key: NEXUS_USERNAME + name: android-passwords + optional: false + - name: PASSWORD + valueFrom: + secretKeyRef: + key: PASSWORD + name: android-passwords + optional: false + - name: SONARQUBE_TOKEN + valueFrom: + secretKeyRef: + key: sonarqube + name: dependency-check-credentials + optional: false + - name: APK_SIGN_KEYSTORE_ALIAS_BDR + value: androiddebugkey + - name: APK_SIGN_KEYSTORE_BDR + value: /home/governikus/bdr.keystore + - name: APK_SIGN_KEYSTORE_PSW_BDR + value: android + - name: CCACHE_REMOTE_STORAGE + value: redis://ausweisapp-redis.govkg.de:32379 + - name: CCACHE_REMOTE_ONLY + value: 'true' + - name: APK_SIGN_KEYSTORE_PSW_UPLOAD + valueFrom: + secretKeyRef: + key: APK_SIGN_KEYSTORE_PSW_UPLOAD + name: android-passwords + optional: false + - name: APK_SIGN_KEYSTORE_ALIAS_UPLOAD + value: androiddebugkey + - name: APK_SIGN_KEYSTORE_UPLOAD + value: /home/governikus/upload.keystore + - name: GPG_PSW + valueFrom: + secretKeyRef: + key: GPG_PSW + name: gpg + optional: false + - name: GPG_ID + valueFrom: + secretKeyRef: + key: GPG_ID + name: gpg + optional: false + - name: CENTRAL_TOKEN + valueFrom: + secretKeyRef: + key: CENTRAL_TOKEN + name: android-passwords + optional: false + image: dev-docker.govkg.de/ausweisapp2/ubuntu:android + imagePullPolicy: Always + lifecycle: + postStart: + exec: + command: + - /bin/sh + - '-c' + - | + base64 -d /secrets/debug.keystore > /home/governikus/.android/debug.keystore && + base64 -d /secrets/release.keystore > /home/governikus/release.keystore && + base64 -d /secrets/bdr.keystore > /home/governikus/bdr.keystore && + base64 -d /secrets/upload.keystore > /home/governikus/upload.keystore && + gpg --batch --passphrase $GPG_PSW --pinentry-mode loopback --import /gpg/GPG_PRIVATE && + gpg --batch --passphrase $GPG_PSW --pinentry-mode loopback --export-secret-keys > ~/.gnupg/secring + name: android + resources: + limits: + cpu: '10' + memory: 16Gi + requests: + cpu: 1500m + memory: 8Gi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - NET_BIND_SERVICE + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: false + runAsUser: 1111 + stdin: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true + volumeMounts: + - mountPath: /secrets + name: keystores + - mountPath: /gpg name: gpg - optional: false - - name: GPG_ID - valueFrom: - secretKeyRef: - key: GPG_ID - name: gpg - optional: false - - name: CENTRAL_PSW - valueFrom: - secretKeyRef: - key: CENTRAL_PSW - name: android-passwords - optional: false - - name: CENTRAL_USERNAME - valueFrom: - secretKeyRef: - key: CENTRAL_USERNAME - name: android-passwords - optional: false - image: dev-docker.governikus.de/ausweisapp2/ubuntu:android - imagePullPolicy: Always - lifecycle: - postStart: - exec: - command: - - /bin/sh - - -c - - base64 -d /secrets/debug.keystore > /home/governikus/.android/debug.keystore - && base64 -d /secrets/release.keystore > /home/governikus/release.keystore - && gpg --batch --passphrase $GPG_PSW --pinentry-mode loopback --import /gpg/GPG_PRIVATE - name: android - resources: - limits: - cpu: "10" - memory: 8Gi - requests: - cpu: 500m - memory: 4Gi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - NET_BIND_SERVICE - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 1000 - runAsNonRoot: true - runAsUser: 1000 - stdin: true - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - tty: true - volumeMounts: - - mountPath: /secrets - name: keystores - - mountPath: /ccache - name: ccache + readOnly: true + dnsPolicy: ClusterFirst restartPolicy: Always + schedulerName: default-scheduler + securityContext: + runAsGroup: 2000 + runAsUser: 3000 terminationGracePeriodSeconds: 30 volumes: - - name: keystores - secret: - defaultMode: 511 - items: - - key: ausweisapp.apk.developer.keystore.jks - mode: 292 - path: debug.keystore - - key: autentapp2.apk.keystore.jks - mode: 292 - path: release.keystore - optional: false - secretName: ausweisapp2-apk-keystores - - name: ccache - persistentVolumeClaim: - claimName: ccache-android - - name: gpg - secret: - defaultMode: 420 - optional: false - secretName: gpg + - name: keystores + secret: + defaultMode: 511 + items: + - key: ausweisapp.apk.developer.keystore.jks + mode: 292 + path: debug.keystore + - key: autentapp2.apk.keystore.jks + mode: 292 + path: release.keystore + - key: ausweisapp.apk.bdr_distribution_developer.keystore.jks + mode: 292 + path: bdr.keystore + - key: ausweisapp.aab.uploader.keystore.jks + mode: 292 + path: upload.keystore + optional: false + secretName: ausweisapp2-apk-keystores + - name: gpg + secret: + defaultMode: 420 + optional: false + secretName: gpg diff -Nru ausweisapp2-2.3.1/resources/jenkins/kubernetes/common.yaml ausweisapp2-2.4.0/resources/jenkins/kubernetes/common.yaml --- ausweisapp2-2.3.1/resources/jenkins/kubernetes/common.yaml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/kubernetes/common.yaml 2025-10-30 10:10:48.000000000 +0000 @@ -1,66 +1,90 @@ apiVersion: apps/v1 kind: Deployment metadata: + labels: + cattle.io/creator: norman + workload.user.cattle.io/workloadselector: deployment-ausweisapp-common name: common namespace: ausweisapp spec: + progressDeadlineSeconds: 600 replicas: 1 + revisionHistoryLimit: 10 selector: matchLabels: workload.user.cattle.io/workloadselector: deployment-ausweisapp-common strategy: type: Recreate template: + metadata: + labels: + workload.user.cattle.io/workloadselector: deployment-ausweisapp-common + namespace: ausweisapp spec: containers: - - env: - - name: EXECUTOR - value: "3" - - name: PASSWORD - valueFrom: - secretKeyRef: - key: PASSWORD - name: android-passwords - optional: false - - name: GPG_PSW - valueFrom: - secretKeyRef: - key: GPG_PSW + - env: + - name: EXECUTOR + value: '5' + - name: FINGERPRINTS + valueFrom: + secretKeyRef: + key: FINGERPRINT + name: generic-secrets + optional: false + - name: PASSWORD + valueFrom: + secretKeyRef: + key: PASSWORD + name: android-passwords + optional: false + - name: GPG_PSW + valueFrom: + secretKeyRef: + key: GPG_PSW + name: gpg + optional: false + - name: GPG_ID + valueFrom: + secretKeyRef: + key: GPG_ID + name: gpg + optional: false + image: dev-docker.governikus.de/ausweisapp2/alpine:common + imagePullPolicy: Always + lifecycle: + postStart: + exec: + command: + - sh + - '-c' + - >- + gpg --batch --passphrase $GPG_PSW --pinentry-mode loopback --import /gpg/GPG_PRIVATE + name: common + resources: + limits: + cpu: '3' + memory: 2Gi + requests: + cpu: 500m + memory: 2Gi + securityContext: + allowPrivilegeEscalation: false + capabilities: {} + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: false + stdin: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true + volumeMounts: + - mountPath: /gpg name: gpg - optional: false - - name: GPG_ID - valueFrom: - secretKeyRef: - key: GPG_ID - name: gpg - optional: false - image: dev-docker.governikus.de/ausweisapp2/alpine:common - imagePullPolicy: Always - lifecycle: - postStart: - exec: - command: - - sh - - '-c' - - >- - gpg --batch --passphrase $GPG_PSW --pinentry-mode loopback --import /gpg/GPG_PRIVATE - name: common - resources: - limits: - cpu: "3" - memory: 2Gi - requests: - cpu: 500m - memory: 2Gi - securityContext: - allowPrivilegeEscalation: false - capabilities: {} - privileged: false - readOnlyRootFilesystem: false - runAsNonRoot: false - stdin: true - tty: true + readOnly: true + dnsPolicy: ClusterFirst restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} terminationGracePeriodSeconds: 30 volumes: - name: gpg diff -Nru ausweisapp2-2.3.1/resources/jenkins/kubernetes/docs.yaml ausweisapp2-2.4.0/resources/jenkins/kubernetes/docs.yaml --- ausweisapp2-2.3.1/resources/jenkins/kubernetes/docs.yaml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/kubernetes/docs.yaml 2025-10-30 10:10:48.000000000 +0000 @@ -1,46 +1,63 @@ apiVersion: apps/v1 kind: Deployment metadata: + labels: + cattle.io/creator: norman + workload.user.cattle.io/workloadselector: deployment-ausweisapp-docs name: docs namespace: ausweisapp spec: + progressDeadlineSeconds: 600 replicas: 1 + revisionHistoryLimit: 10 selector: matchLabels: workload.user.cattle.io/workloadselector: deployment-ausweisapp-docs strategy: type: Recreate template: + metadata: + labels: + workload.user.cattle.io/workloadselector: deployment-ausweisapp-docs spec: containers: - - env: - - name: EXECUTOR - value: "3" - - name: PASSWORD - valueFrom: - secretKeyRef: - key: PASSWORD - name: android-passwords - optional: false - image: dev-docker.governikus.de/ausweisapp2/alpine:docs - imagePullPolicy: Always - name: docs - resources: - limits: - cpu: "3" - memory: 2Gi - requests: - cpu: 500m - memory: 2Gi - securityContext: - allowPrivilegeEscalation: false - capabilities: {} - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 1000 - runAsNonRoot: true - runAsUser: 1000 - stdin: true - tty: true + - env: + - name: FINGERPRINTS + valueFrom: + secretKeyRef: + key: FINGERPRINT + name: generic-secrets + optional: false + - name: PASSWORD + valueFrom: + secretKeyRef: + key: PASSWORD + name: android-passwords + optional: false + image: dev-docker.governikus.de/ausweisapp2/alpine:docs + imagePullPolicy: Always + name: docs + resources: + limits: + cpu: '3' + memory: 2Gi + requests: + cpu: 500m + memory: 2Gi + securityContext: + allowPrivilegeEscalation: false + capabilities: {} + privileged: false + readOnlyRootFilesystem: false + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + stdin: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true + dnsPolicy: ClusterFirst restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} terminationGracePeriodSeconds: 30 diff -Nru ausweisapp2-2.3.1/resources/jenkins/kubernetes/format_files.sh ausweisapp2-2.4.0/resources/jenkins/kubernetes/format_files.sh --- ausweisapp2-2.3.1/resources/jenkins/kubernetes/format_files.sh 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/kubernetes/format_files.sh 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,29 @@ +#!/bin/bash + +find . -type f \( -name "*.yaml" -o -name "*.yml" \) | while read -r file; do + echo "Processing: $file" + + # Generated by other tools, ignored by kubernetes + yq -iY 'del(.metadata.annotations)' "$file" + # Generated values, readonly + yq -iY 'del(.metadata.creationTimestamp)' "$file" + yq -iY 'del(.metadata.generation)' "$file" + yq -iY 'del(.metadata.uid)' "$file" + # Only needed for a client to request changes applied after resourceVersion xyz. + yq -iY 'del(.metadata.resourceVersion)' "$file" + # Kubernetes uses this to keep track of who has last modified a certain field + yq -iY 'del(.metadata.managedFields)' "$file" + + # Generated by other tools, ignored by kubernetes + yq -iY 'del(.spec.template.metadata.annotations)' "$file" + # Generated values, readonly + yq -iY 'del(.spec.template.metadata.creationTimestamp)' "$file" + + # Only contains a soft rule + yq -iY 'del(.spec.template.spec.affinity)' "$file" + + # Readonly + yq -iY 'del(.status)' "$file" + + yamlfmt "$file" +done diff -Nru ausweisapp2-2.3.1/resources/jenkins/kubernetes/linux.yaml ausweisapp2-2.4.0/resources/jenkins/kubernetes/linux.yaml --- ausweisapp2-2.3.1/resources/jenkins/kubernetes/linux.yaml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/kubernetes/linux.yaml 2025-10-30 10:10:48.000000000 +0000 @@ -1,65 +1,80 @@ apiVersion: apps/v1 kind: Deployment metadata: + labels: + cattle.io/creator: norman + workload.user.cattle.io/workloadselector: deployment-ausweisapp-linux name: linux namespace: ausweisapp spec: + progressDeadlineSeconds: 600 replicas: 3 + revisionHistoryLimit: 10 selector: matchLabels: workload.user.cattle.io/workloadselector: deployment-ausweisapp-linux strategy: - type: Recreate + rollingUpdate: + maxSurge: 0 + maxUnavailable: 25% + type: RollingUpdate template: + metadata: + labels: + workload.user.cattle.io/workloadselector: deployment-ausweisapp-linux spec: containers: - - env: - - name: EXECUTOR - value: "2" - - name: CCACHE_DIR - value: /ccache - - name: CCACHE_MAXSIZE - value: 10G - - name: CMAKE_BUILD_PARALLEL_LEVEL - value: 5 - - name: CTEST_PARALLEL_LEVEL - value: 5 - - name: PROCESSOR_COUNT - value: 5 - - name: PASSWORD - valueFrom: - secretKeyRef: - key: PASSWORD - name: android-passwords - optional: false - image: dev-docker.governikus.de/ausweisapp2/alpine:linux - imagePullPolicy: Always - name: linux - resources: - limits: - cpu: "10" - memory: 8Gi - requests: - cpu: 500m - memory: 2Gi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - NET_BIND_SERVICE - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 1000 - runAsNonRoot: true - runAsUser: 1000 - stdin: true - tty: true - volumeMounts: - - mountPath: /ccache - name: ccache + - env: + - name: CCACHE_MAXSIZE + value: 10G + - name: CMAKE_BUILD_PARALLEL_LEVEL + value: '5' + - name: CTEST_PARALLEL_LEVEL + value: '5' + - name: EXECUTOR + value: '2' + - name: FINGERPRINTS + valueFrom: + secretKeyRef: + key: FINGERPRINT + name: generic-secrets + optional: false + - name: PASSWORD + valueFrom: + secretKeyRef: + key: PASSWORD + name: android-passwords + optional: false + - name: CCACHE_REMOTE_STORAGE + value: redis://ausweisapp-redis.govkg.de:32379 + - name: CCACHE_REMOTE_ONLY + value: 'true' + image: dev-docker.governikus.de/ausweisapp2/alpine:linux + imagePullPolicy: Always + name: linux + resources: + limits: + cpu: '10' + memory: 12Gi + requests: + cpu: 500m + memory: 10Gi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - NET_BIND_SERVICE + privileged: false + readOnlyRootFilesystem: false + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + stdin: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true + dnsPolicy: ClusterFirst restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} terminationGracePeriodSeconds: 30 - volumes: - - name: ccache - persistentVolumeClaim: - claimName: ccache-linux diff -Nru ausweisapp2-2.3.1/resources/jenkins/kubernetes/redis.yaml ausweisapp2-2.4.0/resources/jenkins/kubernetes/redis.yaml --- ausweisapp2-2.3.1/resources/jenkins/kubernetes/redis.yaml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/kubernetes/redis.yaml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + namespace: ausweisapp +spec: + progressDeadlineSeconds: 600 + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + app: redis + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: redis + name: redis + spec: + containers: + - args: + - /data/redis.conf + image: redis:alpine + imagePullPolicy: Always + name: redis + ports: + - containerPort: 6379 + name: 6379tcp02 + protocol: TCP + resources: + limits: + cpu: '10' + memory: 40Gi + requests: + cpu: 500m + memory: 8Gi + securityContext: + capabilities: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /data + name: redis-data + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + volumes: + - name: redis-data + persistentVolumeClaim: + claimName: redis-data diff -Nru ausweisapp2-2.3.1/resources/jenkins/kubernetes/sync.yaml ausweisapp2-2.4.0/resources/jenkins/kubernetes/sync.yaml --- ausweisapp2-2.3.1/resources/jenkins/kubernetes/sync.yaml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/kubernetes/sync.yaml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,67 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + workload.user.cattle.io/workloadselector: apps.deployment-ausweisapp-sync + name: sync + namespace: ausweisapp +spec: + progressDeadlineSeconds: 600 + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + workload.user.cattle.io/workloadselector: apps.deployment-ausweisapp-sync + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + workload.user.cattle.io/workloadselector: apps.deployment-ausweisapp-sync + namespace: ausweisapp + spec: + containers: + - env: + - name: FINGERPRINTS + valueFrom: + secretKeyRef: + key: FINGERPRINT + name: generic-secrets + optional: false + - name: PASSWORD + valueFrom: + secretKeyRef: + key: SWARM_PASSWORD + name: generic-secrets + optional: false + image: dev-docker.governikus.de/ausweisapp2/alpine:sync + imagePullPolicy: Always + name: sync + resources: + requests: + cpu: '5' + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: false + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /ssh + name: ssh + readOnly: true + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + volumes: + - name: ssh + secret: + defaultMode: 420 + optional: false + secretName: generic-secrets diff -Nru ausweisapp2-2.3.1/resources/jenkins/kubernetes/trigger.yaml ausweisapp2-2.4.0/resources/jenkins/kubernetes/trigger.yaml --- ausweisapp2-2.3.1/resources/jenkins/kubernetes/trigger.yaml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/kubernetes/trigger.yaml 2025-10-30 10:10:48.000000000 +0000 @@ -1,46 +1,73 @@ apiVersion: apps/v1 kind: Deployment metadata: + labels: + cattle.io/creator: norman + workload.user.cattle.io/workloadselector: deployment-ausweisapp-trigger name: trigger namespace: ausweisapp spec: + progressDeadlineSeconds: 600 replicas: 1 + revisionHistoryLimit: 10 selector: matchLabels: workload.user.cattle.io/workloadselector: deployment-ausweisapp-trigger strategy: type: Recreate template: + metadata: + labels: + workload.user.cattle.io/workloadselector: deployment-ausweisapp-trigger spec: containers: - - env: - - name: EXECUTOR - value: "3" - - name: PASSWORD - valueFrom: - secretKeyRef: - key: PASSWORD - name: android-passwords - optional: false - image: dev-docker.governikus.de/ausweisapp2/alpine:trigger - imagePullPolicy: Always - name: trigger - resources: - limits: - cpu: "1" - memory: 2Gi - requests: - cpu: 500m - memory: 2Gi - securityContext: - allowPrivilegeEscalation: false - capabilities: {} - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 1000 - runAsNonRoot: true - runAsUser: 1000 - stdin: true - tty: true + - env: + - name: PASSWORD + valueFrom: + secretKeyRef: + key: PASSWORD + name: android-passwords + - name: REVIEWBOARD_TOKEN + valueFrom: + secretKeyRef: + key: REVIEWBOARD_TOKEN + name: reviewboard + optional: false + - name: REVIEWBOARD_USER + valueFrom: + secretKeyRef: + key: REVIEWBOARD_USER + name: reviewboard + optional: false + - name: FINGERPRINTS + valueFrom: + secretKeyRef: + key: FINGERPRINT + name: generic-secrets + optional: false + image: dev-docker.governikus.de/ausweisapp2/alpine:trigger + imagePullPolicy: Always + name: trigger + resources: + limits: + cpu: '1' + memory: 2Gi + requests: + cpu: 500m + memory: 2Gi + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: false + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + stdin: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true + dnsPolicy: ClusterFirst restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} terminationGracePeriodSeconds: 30 diff -Nru ausweisapp2-2.3.1/resources/jenkins/kubernetes/vanilla.yaml ausweisapp2-2.4.0/resources/jenkins/kubernetes/vanilla.yaml --- ausweisapp2-2.3.1/resources/jenkins/kubernetes/vanilla.yaml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/jenkins/kubernetes/vanilla.yaml 2025-10-30 10:10:48.000000000 +0000 @@ -1,83 +1,98 @@ apiVersion: apps/v1 kind: Deployment metadata: + labels: + cattle.io/creator: norman + workload.user.cattle.io/workloadselector: deployment-ausweisapp-vanilla name: vanilla namespace: ausweisapp spec: + progressDeadlineSeconds: 600 replicas: 1 + revisionHistoryLimit: 10 selector: matchLabels: workload.user.cattle.io/workloadselector: deployment-ausweisapp-vanilla strategy: type: Recreate template: + metadata: + labels: + workload.user.cattle.io/workloadselector: deployment-ausweisapp-vanilla spec: containers: - - env: - - name: EXECUTOR - value: "2" - - name: CCACHE_DIR - value: /ccache - - name: CCACHE_MAXSIZE - value: 10G - - name: CMAKE_BUILD_PARALLEL_LEVEL - value: 5 - - name: CTEST_PARALLEL_LEVEL - value: 5 - - name: PROCESSOR_COUNT - value: 5 - - name: PASSWORD - valueFrom: - secretKeyRef: - key: PASSWORD - name: android-passwords - optional: false - - name: DEPENDENCY_CHECK_PASSWORD - valueFrom: - secretKeyRef: - key: password - name: dependency-check-credentials - optional: false - - name: DEPENDENCY_CHECK_USER - valueFrom: - secretKeyRef: - key: username - name: dependency-check-credentials - optional: false - - name: SONARQUBE_TOKEN - valueFrom: - secretKeyRef: - key: sonarqube - name: dependency-check-credentials - optional: false - image: dev-docker.governikus.de/ausweisapp2/ubuntu:vanilla - imagePullPolicy: Always - name: vanilla - resources: - limits: - cpu: "10" - memory: 8Gi - requests: - cpu: 500m - memory: 2Gi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - NET_BIND_SERVICE - privileged: false - readOnlyRootFilesystem: false - runAsGroup: 1000 - runAsNonRoot: true - runAsUser: 1000 - stdin: true - tty: true - volumeMounts: - - mountPath: /ccache - name: ccache + - env: + - name: CCACHE_MAXSIZE + value: 10G + - name: CMAKE_BUILD_PARALLEL_LEVEL + value: '5' + - name: CTEST_PARALLEL_LEVEL + value: '5' + - name: EXECUTOR + value: '2' + - name: PASSWORD + valueFrom: + secretKeyRef: + key: PASSWORD + name: android-passwords + optional: false + - name: DEPENDENCY_CHECK_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: dependency-check-credentials + optional: false + - name: DEPENDENCY_CHECK_USER + valueFrom: + secretKeyRef: + key: username + name: dependency-check-credentials + optional: false + - name: SONAR_TOKEN + valueFrom: + secretKeyRef: + key: sonarqube + name: dependency-check-credentials + optional: false + - name: FINGERPRINTS + valueFrom: + secretKeyRef: + key: FINGERPRINT + name: generic-secrets + optional: false + - name: CCACHE_REMOTE_STORAGE + value: redis://ausweisapp-redis.govkg.de:32379 + - name: CCACHE_REMOTE_ONLY + value: 'true' + - name: SONAR_HOST_URL + value: 'https://sonar.govkg.de' + image: dev-docker.governikus.de/ausweisapp2/ubuntu:vanilla + imagePullPolicy: Always + name: vanilla + resources: + limits: + cpu: '10' + memory: 8Gi + requests: + cpu: 500m + memory: 2Gi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - NET_BIND_SERVICE + privileged: false + readOnlyRootFilesystem: false + runAsUser: 1111 + stdin: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true + dnsConfig: {} + dnsPolicy: ClusterFirst restartPolicy: Always + schedulerName: default-scheduler + securityContext: + runAsGroup: 2000 + runAsUser: 3000 terminationGracePeriodSeconds: 30 - volumes: - - name: ccache - persistentVolumeClaim: - claimName: ccache-vanilla diff -Nru ausweisapp2-2.3.1/resources/packaging/android/AndroidManifest.xml.apk.in ausweisapp2-2.4.0/resources/packaging/android/AndroidManifest.xml.apk.in --- ausweisapp2-2.3.1/resources/packaging/android/AndroidManifest.xml.apk.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/android/AndroidManifest.xml.apk.in 2025-10-30 10:10:48.000000000 +0000 @@ -69,7 +69,7 @@ - - - - - + diff -Nru ausweisapp2-2.3.1/resources/packaging/android/lint.apk.smarteid.xml ausweisapp2-2.4.0/resources/packaging/android/lint.apk.smarteid.xml --- ausweisapp2-2.3.1/resources/packaging/android/lint.apk.smarteid.xml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/android/lint.apk.smarteid.xml 2025-10-30 10:10:48.000000000 +0000 @@ -1,16 +1,5 @@ - - - - - + @@ -73,7 +62,7 @@ errorLine2=" ~~~~~~~~~~~~~~"> @@ -84,19 +73,8 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> - - - - + column="12"/> @@ -117,7 +95,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -128,7 +106,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~"> diff -Nru ausweisapp2-2.3.1/resources/packaging/android/lint.apk.xml ausweisapp2-2.4.0/resources/packaging/android/lint.apk.xml --- ausweisapp2-2.3.1/resources/packaging/android/lint.apk.xml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/android/lint.apk.xml 2025-10-30 10:10:48.000000000 +0000 @@ -1,16 +1,5 @@ - - - - - + @@ -41,7 +30,7 @@ errorLine2=" ~~~~~~~~~~~~~~"> @@ -52,19 +41,8 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~"> - - - - + column="12"/> @@ -85,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -96,7 +74,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~"> diff -Nru ausweisapp2-2.3.1/resources/packaging/android/res/values-night/style.xml ausweisapp2-2.4.0/resources/packaging/android/res/values-night/style.xml --- ausweisapp2-2.3.1/resources/packaging/android/res/values-night/style.xml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/android/res/values-night/style.xml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,13 @@ + + #232323 + + + + + diff -Nru ausweisapp2-2.3.1/resources/packaging/ios/launchscreen.storyboard ausweisapp2-2.4.0/resources/packaging/ios/launchscreen.storyboard --- ausweisapp2-2.3.1/resources/packaging/ios/launchscreen.storyboard 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/ios/launchscreen.storyboard 2025-10-30 10:10:48.000000000 +0000 @@ -22,7 +22,7 @@ - + diff -Nru ausweisapp2-2.3.1/resources/packaging/linux/com.governikus.ausweisapp2.desktop.in ausweisapp2-2.4.0/resources/packaging/linux/com.governikus.ausweisapp2.desktop.in --- ausweisapp2-2.3.1/resources/packaging/linux/com.governikus.ausweisapp2.desktop.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/linux/com.governikus.ausweisapp2.desktop.in 2025-10-30 10:10:48.000000000 +0000 @@ -7,6 +7,6 @@ Terminal=false Categories=System;Security; GenericName=Authentication App -Keywords=nPA,eID,eAT,Personalausweis,Aufenthaltstitel,Identity,Card +Keywords=nPA;eID;eAT;Personalausweis;Aufenthaltstitel;Identity;Card Name=AusweisApp StartupWMClass=AusweisApp diff -Nru ausweisapp2-2.3.1/resources/packaging/updater/Appcast.item.json.in ausweisapp2-2.4.0/resources/packaging/updater/Appcast.item.json.in --- ausweisapp2-2.3.1/resources/packaging/updater/Appcast.item.json.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/updater/Appcast.item.json.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ - { - "date": "APPCAST_DATE", - "platform": "APPCAST_PLATFORM", - "minimum_platform": "APPCAST_MINIMUM_PLATFORM", - "version": "APPCAST_VERSION", - "url": "APPCAST_URL", - "size": APPCAST_SIZE, - "checksum": "APPCAST_CHECKSUM", - "notes": "APPCAST_NOTES" - } diff -Nru ausweisapp2-2.3.1/resources/packaging/updater/Appcast.json.in ausweisapp2-2.4.0/resources/packaging/updater/Appcast.json.in --- ausweisapp2-2.3.1/resources/packaging/updater/Appcast.json.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/updater/Appcast.json.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -{ - "items": - [ -@APPCAST_ITEMS@ - ] -} diff -Nru ausweisapp2-2.3.1/resources/packaging/win/WIX.Texts.de-DE.wxl ausweisapp2-2.4.0/resources/packaging/win/WIX.Texts.de-DE.wxl --- ausweisapp2-2.3.1/resources/packaging/win/WIX.Texts.de-DE.wxl 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/win/WIX.Texts.de-DE.wxl 2025-10-30 10:10:48.000000000 +0000 @@ -1,57 +1,59 @@ - - - {\WixUI_Font_Bigger}Der Setup-Assistent der [ProductName] wurde vorzeitig beendet. - Der Setup-Assistent der [ProductName] wurde aufgrund eines Fehlers vorzeitig beendet. Das System wurde nicht verändert. Sie müssen den Setup-Assistenten erneut ausführen, um dieses Programm zu einem späteren Zeitpunkt zu installieren. - Warten Sie, während die [ProductName] vom Setup-Assistenten installiert wird. - {\WixUI_Font_Title}Die [ProductName] wird installiert - Warten Sie, während die [ProductName] vom Setup-Assistenten geändert wird. - {\WixUI_Font_Title}Die [ProductName] wird geändert - Warten Sie, während die [ProductName] vom Setup-Assistenten repariert wird. - {\WixUI_Font_Title}Die [ProductName] wird repariert - Warten Sie, während die [ProductName] vom Setup-Assistenten entfernt wird. - {\WixUI_Font_Title}Die [ProductName] wird entfernt - Der Setup-Assistent aktualisiert die [ProductName]. Bitte warten Sie. - {\WixUI_Font_Title}Die [ProductName] wird aktualisiert - Die Installation der [ProductName] wird vom Setup-Assistenten auf dem Computer abgeschlossen. Klicken Sie auf "Installieren", um den Vorgang fortzusetzen, bzw. auf "Abbrechen", um den Setup-Assistenten zu beenden. - {\WixUI_Font_Bigger}Der Setup-Assistent der [ProductName] wird fortgesetzt - {\WixUI_Font_Bigger}Die Installation der [ProductName] wurde abgebrochen. - {\WixUI_Font_Title}Bereit zur Installation der [ProductName] - {\WixUI_Font_Title}Bereit zur Änderung der [ProductName] - Klicken Sie auf "Reparieren", um die Installation der [ProductName] zu reparieren. Klicken Sie auf "Zurück", um die Installationseinstellungen zu prüfen oder zu ändern. Klicken Sie auf "Abbrechen", um den Assistenten zu beenden. - {\WixUI_Font_Title}Bereit zur Reparatur der [ProductName] - Klicken Sie auf "Entfernen", um die [ProductName] vom Computer zu entfernen. Klicken Sie auf "Zurück", um die Installationseinstellungen zu prüfen oder zu ändern. Klicken Sie auf "Abbrechen", um den Assistenten zu beenden. - {\WixUI_Font_Title}Bereit zum Entfernen der [ProductName] - Klicken Sie auf "Aktualisieren", um die [ProductName] auf Ihrem Computer zu aktualisieren. Klicken Sie auf "Zurück", um die Installationseinstellungen zu prüfen oder zu ändern. Klicken Sie auf "Abbrechen", um den Assistenten zu beenden. - {\WixUI_Font_Title}Bereit zum Aktualisieren der [ProductName] - {\WixUI_Font_Bigger}Willkommen beim Setup-Assistenten der [ProductName] - Die [ProductName] verfügt nicht über unabhängig voneinander auswählbare Funktionen. - Entfernt die [ProductName] vom Computer. - Die [ProductName] kann nicht entfernt werden. - Die [ProductName] kann nicht repariert werden. - {\WixUI_Font_Bigger}Die Installation der [ProductName] ist abgeschlossen. - Die [ProductName] wird auf dem Computer installiert. Klicken Sie auf "Weiter", um den Vorgang fortzusetzen, oder auf "Abbrechen", um den Setup-Assistenten zu beenden. - Die [ProductName] wird auf dem Computer aktualisiert. Klicken Sie auf "Weiter", um den Vorgang fortzusetzen, oder auf "Abbrechen", um den Setup-Assistenten zu beenden. - {\WixUI_Font_Bigger}Willkommen beim Setup-Assistenten der [ProductName] - Möchten Sie die Installation der [ProductName] wirklich abbrechen? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - 1031 - Ausweis, Authentisierung - Installationspaket für die AusweisApp - Offizieller eID-Client des Bundes - https://www.ausweisapp.bund.de/de/aa2/support - https://www.ausweisapp.bund.de - https://www.ausweisapp.bund.de/de/aa2/download - Eine aktuellere Version der [ProductName] ist bereits installiert. Die Installation wird nun beendet. - Ihr Betriebssystem erfüllt leider nicht die Mindestvoraussetzungen für die AusweisApp. Sie benötigen mindestens eine 64-Bit-Version von Windows 10 oder Windows Server 2016. - Ihr Betriebssystem erfüllt leider nicht die Mindestvoraussetzungen für die AusweisApp. Sie benötigen mindestens eine 64-Bit-Version von Windows 10 Version 1809. - Ihr Betriebssystem erfüllt leider nicht die Mindestvoraussetzungen für die AusweisApp. Sie benötigen mindestens eine 64-Bit-Version von Windows Server 2016 Version 1607. - AusweisApp, der eID-Client der Governikus KG - Ermöglicht den parallelen Betrieb mehrere Instanzen der AusweisApp auf einem System im Anwendungsservermodus. - Installationsoptionen - - Verknüpfung auf dem Desktop anlegen - Proxy-Dienst installieren - Notwendige Regeln zur Windows-Firewall hinzufügen - Firewallregeln sind u.A. für die Funktion Smartphone als Kartenleser notwendig. - Starten der AusweisApp + + + + + + + + + + + + + + + + + + + + + + + diff -Nru ausweisapp2-2.3.1/resources/packaging/win/WIX.Texts.en-US.wxl ausweisapp2-2.4.0/resources/packaging/win/WIX.Texts.en-US.wxl --- ausweisapp2-2.3.1/resources/packaging/win/WIX.Texts.en-US.wxl 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/win/WIX.Texts.en-US.wxl 2025-10-30 10:10:48.000000000 +0000 @@ -1,16 +1,15 @@ - - - 1033 - A later version of [ProductName] is already installed. Setup will now exit. - Your operating system does not meet the minimum requirements for AusweisApp. You need at least a 64-bit version of Windows 10 or Windows Server 2016. - Your operating system does not meet the minimum requirements for AusweisApp. You need at least a 64-bit version of Windows 10 version 1809. - Your operating system does not meet the minimum requirements for AusweisApp. You need at least a 64-bit version of Windows Server 2016 version 1607. - AusweisApp, the eID-Client of Governikus KG - Installation options - - Create link on desktop - The installation takes place on a Windows Terminal Server. Please take note of the information in the documentation on installations in company networks. - Add required rules to Windows Firewall - Firewall rules are required, among others, by the function smartphone as card reader. - Start AusweisApp + + + + + + + + + + + + + + diff -Nru ausweisapp2-2.3.1/resources/packaging/win/WIX.template.in ausweisapp2-2.4.0/resources/packaging/win/WIX.template.in --- ausweisapp2-2.3.1/resources/packaging/win/WIX.template.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/win/WIX.template.in 2025-10-30 10:10:48.000000000 +0000 @@ -1,92 +1,73 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - = 1000) OR (VersionNT64 = 603 AND WIN10FOUND)]]> - - - - - - - - - 1) OR Installed OR NOT (WIN10RELEASEID) OR (WIN10RELEASEID >= 1809)]]> - - - - = 1809) OR (WIN10RELEASEID = 1607)]]> - - - - - - - - - ProductIcon.ico - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - + + diff -Nru ausweisapp2-2.3.1/resources/packaging/win/executable.wxs ausweisapp2-2.4.0/resources/packaging/win/executable.wxs --- ausweisapp2-2.3.1/resources/packaging/win/executable.wxs 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/win/executable.wxs 2025-10-30 10:10:48.000000000 +0000 @@ -1,62 +1,34 @@ - - - - - - - - - - - - - (NOT PROXYSERVICE) AND ( - ((WIX_UPGRADE_DETECTED OR REINSTALL) AND (PROXYSERVICE_FOUND OR PROXYSERVICE_OLD_FOUND)) - OR (NOT (WIX_UPGRADE_DETECTED OR REINSTALL) AND (WIX_SUITE_TERMINAL="1") AND NOT (WIX_SUITE_SINGLEUSERTS)) - ) - - - - PROXYSERVICE = "true" - - - NOT (PROXYSERVICE = "true") - - - - - - - - - - NOT (PROXYSERVICE = "true") - - - - - PROXYSERVICE = "true" - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru ausweisapp2-2.3.1/resources/packaging/win/gui.wxs ausweisapp2-2.4.0/resources/packaging/win/gui.wxs --- ausweisapp2-2.3.1/resources/packaging/win/gui.wxs 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/win/gui.wxs 2025-10-30 10:10:48.000000000 +0000 @@ -1,127 +1,122 @@ - - - - - - - - - - - - - - - - - - - - - - - - - 1 - "1"]]> - - (LaunchGui = "true") AND NOT Installed - 1 - - NOT Installed - Installed AND PATCH - 1 - - 1 - LicenseAccepted = "1" - - 1 - NOT (DesktopShortcutGui = "true") - DesktopShortcutGui = "true" - NOT (SystemSettingsGui = "true") - SystemSettingsGui = "true" - NOT (ProxyServiceGui = "true") - ProxyServiceGui = "true" - 1 - NOT WIXUI_DONTVALIDATEPATH - "1"]]> - WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1" - 1 - 1 - - NOT Installed - Installed AND NOT PATCH - Installed AND PATCH - - 1 - - 1 - 1 - 1 - - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - NOT((WIX_SUITE_TERMINAL="1") AND NOT (WIX_SUITE_SINGLEUSERTS)) - (WIX_SUITE_TERMINAL="1") AND NOT (WIX_SUITE_SINGLEUSERTS) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru ausweisapp2-2.3.1/resources/packaging/win/install_settings.wxs ausweisapp2-2.4.0/resources/packaging/win/install_settings.wxs --- ausweisapp2-2.3.1/resources/packaging/win/install_settings.wxs 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/win/install_settings.wxs 2025-10-30 10:10:48.000000000 +0000 @@ -1,166 +1,119 @@ - - - - - - - - - - - - - (NOT SYSTEMSETTINGS) AND ( - ((WIX_UPGRADE_DETECTED OR REINSTALL) AND (SYSTEMSETTINGS_FOUND OR SYSTEMSETTINGS_OLD_FOUND)) - OR (NOT (WIX_UPGRADE_DETECTED OR REINSTALL)) - ) - - - SYSTEMSETTINGS = "true" - - - NOT (SYSTEMSETTINGS = "true") - - - - - - - - - - - - - (NOT DESKTOPSHORTCUT) AND ( - ((WIX_UPGRADE_DETECTED OR REINSTALL) AND (DESKTOPSHORTCUT_FOUND OR DESKTOPSHORTCUT_MIGRATION)) - OR (NOT (WIX_UPGRADE_DETECTED OR REINSTALL)) - ) - - - DESKTOPSHORTCUT = "true" - - - NOT (DESKTOPSHORTCUT = "true") - - - - - - - - - - - - - (NOT STARTMENUSHORTCUT) AND ( - ((WIX_UPGRADE_DETECTED OR REINSTALL) AND (STARTMENUSHORTCUT_FOUND OR STARTMENUSHORTCUT_MIGRATION)) - OR (NOT (WIX_UPGRADE_DETECTED OR REINSTALL)) - ) - - - - - - (NOT LAUNCH) OR LAUNCH = "true" - - - (LAUNCH) AND NOT (LAUNCH = "true") - - - - - - LAUNCH = "true" - - - - - - - - - - - - - - - - SYSTEMSETTINGS = "true" - - - - - - - - - - - - - DESKTOPSHORTCUT = "true" - - - - - - - - - - STARTMENUSHORTCUT = "true" - - - - - - - - - - - - - - - - - - - - - INSTALLDIR_REGISTRY - - - INSTALLDIR - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru ausweisapp2-2.3.1/resources/packaging/win/runtime_settings.wxs ausweisapp2-2.4.0/resources/packaging/win/runtime_settings.wxs --- ausweisapp2-2.3.1/resources/packaging/win/runtime_settings.wxs 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/packaging/win/runtime_settings.wxs 2025-10-30 10:10:48.000000000 +0000 @@ -1,286 +1,207 @@ - - - - - - - - - - - - - - - - - (NOT AUTOSTART) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND AUTOSTART_FOUND - ) - - - (NOT AUTOSTART) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND AUTOSTART_OLD_FOUND - ) - - - - - - - - (NOT TRAYICON) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND TRAYICON_FOUND - ) - - - - - - - - (NOT AUTOHIDE) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND AUTOHIDE_FOUND - ) - - - - - - - - (NOT REMINDTOCLOSE) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND REMINDTOCLOSE_FOUND - ) - - - - - - - - - - - (NOT ASSISTANT) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND ( - (ASSISTANT_FOUND = "DEFAULT" AND NOT SHOWONBOARDING_FOUND) OR - SHOWONBOARDING_FOUND = "false" - ) - ) - - - - - - - - (NOT TRANSPORTPINREMINDER) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND TRANSPORTPINREMINDER_FOUND - ) - - - - - - - - (NOT CUSTOMPROXYTYPE) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND CUSTOMPROXYTYPE_FOUND - ) - - - - - - - - (NOT CUSTOMPROXYHOST) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND CUSTOMPROXYHOST_FOUND - ) - - - - - - - - (NOT CUSTOMPROXYPORT) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND CUSTOMPROXYPORT_FOUND - ) - - - - - - - - (NOT UPDATECHECK) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND UPDATECHECK_FOUND - ) - - - - - - - - (NOT SHUFFLESCREENKEYBOARD) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND SHUFFLESCREENKEYBOARD_FOUND - ) - - - - - - - - (NOT SECURESCREENKEYBOARD) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND SECURESCREENKEYBOARD_FOUND - ) - - - - - - - - (NOT ENABLECANALLOWED) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND ENABLECANALLOWED_FOUND - ) - - - - - - - - (NOT SKIPRIGHTSONCANALLOWED) AND ( - (WIX_UPGRADE_DETECTED OR REINSTALL) AND SKIPRIGHTSONCANALLOWED_FOUND - ) - - - - - - - - - - - - - - - - - - - - - - - - - - AUTOSTART = "true" - - - - - - - TRAYICON - - - - - - - AUTOHIDE - - - - - - - REMINDTOCLOSE - - - - - - - ASSISTANT = "false" - - - - - - - TRANSPORTPINREMINDER - - - - - - - CUSTOMPROXYTYPE - - - - - - - CUSTOMPROXYHOST - - - - - - - CUSTOMPROXYPORT - - - - - - - UPDATECHECK - - - - - - - SHUFFLESCREENKEYBOARD - - - - - - - SECURESCREENKEYBOARD - - - - - - - ENABLECANALLOWED - - - - - - - SKIPRIGHTSONCANALLOWED - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru ausweisapp2-2.3.1/resources/shader/ColorOverlayShader.frag ausweisapp2-2.4.0/resources/shader/ColorOverlayShader.frag --- ausweisapp2-2.3.1/resources/shader/ColorOverlayShader.frag 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/shader/ColorOverlayShader.frag 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -#version 440 -layout(location = 0) in vec2 qt_TexCoord0; -layout(location = 0) out vec4 fragColor; -layout(binding = 1) uniform sampler2D source; -layout(std140, binding = 0) uniform buf { - mat4 qt_Matrix; - float qt_Opacity; - vec4 color; -} ubuf; -void main() -{ - vec4 pixelColor = texture(source, qt_TexCoord0); - fragColor = vec4(mix(pixelColor.rgb/max(pixelColor.a, 0.00390625), ubuf.color.rgb/max(ubuf.color.a, 0.00390625), ubuf.color.a) * pixelColor.a, pixelColor.a) * ubuf.qt_Opacity; -} diff -Nru ausweisapp2-2.3.1/resources/shader/DesaturateShader.frag ausweisapp2-2.4.0/resources/shader/DesaturateShader.frag --- ausweisapp2-2.3.1/resources/shader/DesaturateShader.frag 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/shader/DesaturateShader.frag 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -#version 440 -layout(location = 0) in vec2 qt_TexCoord0; -layout(location = 0) out vec4 fragColor; -layout(binding = 1) uniform sampler2D source; -layout(std140, binding = 0) uniform buf { - mat4 qt_Matrix; - float qt_Opacity; -} ubuf; -void main() -{ - vec4 pixelColor = texture(source, qt_TexCoord0); - float bw = dot(pixelColor.xyz, vec3(0.344, 0.5, 0.156)); - fragColor = vec4(bw, bw, bw, pixelColor.a) * ubuf.qt_Opacity; -} diff -Nru ausweisapp2-2.3.1/resources/sonar-project.properties.in ausweisapp2-2.4.0/resources/sonar-project.properties.in --- ausweisapp2-2.3.1/resources/sonar-project.properties.in 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/sonar-project.properties.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -sonar.host.url=https://sonar.govkg.de - -sonar.projectKey=ausweisapp2 -sonar.projectName=@PROJECT_NAME@ -sonar.projectVersion=@PROJECT_VERSION@ - -sonar.projectBaseDir=@PROJECT_SOURCE_DIR@ -sonar.sources=src -sonar.tests=test - -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.threads=4 -sonar.cfamily.analysisCache.mode=fs -sonar.cfamily.analysisCache.path=@SONAR_CACHE_DIR@ - -sonar.cfamily.reportingCppStandardOverride=c++17 - -sonar.cfamily.compile-commands=@PROJECT_BINARY_DIR@/compile_commands.json -#sonar.cfamily.gcov.reportsPath=@PROJECT_BINARY_DIR@/Testing/CoverageInfo -sonar.coverageReportPaths=@PROJECT_BINARY_DIR@/gcovr_sonarqube.xml -sonar.exclusions=src/external/**,utils/**,**/CMakeFiles/*,**/*.java - -sonar.dependencyCheck.jsonReportPath=@PROJECT_BINARY_DIR@/dependency-check-report.json -sonar.dependencyCheck.htmlReportPath=@PROJECT_BINARY_DIR@/dependency-check-report.html diff -Nru ausweisapp2-2.3.1/resources/translations/ausweisapp_de.ts ausweisapp2-2.4.0/resources/translations/ausweisapp_de.ts --- ausweisapp2-2.3.1/resources/translations/ausweisapp_de.ts 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/translations/ausweisapp_de.ts 2025-10-30 10:10:48.000000000 +0000 @@ -42,9 +42,67 @@ Eine Aktualisierung ist verfügbar (Version %1). - To close the app, press the back button 2 times. + To close the app, tap the back button 2 times. 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). - Zum Schließen erneut "Zurück" drücken. + Zum Schließen erneut "Zurück" tippen. + + + + AuthCanceledView + + This may take a few moments. + LABEL ALL_PLATFORMS + Das kann einen kurzen Moment dauern. + + + The authentication was cancelled successfully. + LABEL ALL_PLATFORMS + Die Authentisierung wurde erfolgreich abgebrochen. + + + Back to provider + LABEL ALL_PLATFORMS + Zurück zum Anbieter + + + Authentication was canceled + LABEL ALL_PLATFORMS + Authentisierung wurde abgebrochen + + + The authentication is being canceled at the provider (%1) + LABEL ALL_PLATFORMS + Die Authentisierung wird seitens des Anbieters (%1) beendet + + + Back to start page + LABEL ALL_PLATFORMS + Zurück zur Startseite + + + The cancellation was triggered on the card reader. + LABEL ALL_PLATFORMS + Der Abbruch wurde auf dem Kartenleser ausgelöst. + + + Back to setup + LABEL ALL_PLATFORM + Zurück zur Einrichtung + + + You may start a new authentication after clicking the button. + LABEL DESKTOP + Sie können eine neue Authentisierung starten, nachdem Sie den Button geklickt haben. + + + You may start a new authentication after tapping the button. + LABEL ANDROID IOS + Sie können eine neue Authentisierung starten, nachdem Sie den Button getippt haben. + + + Cancellation progress + LABEL ALL_PLATFORMS Name of an progress indicator during the cancellation of an authentication read by screen readers + Fortschritt des Abbruchs @@ -146,9 +204,14 @@ - bewegen Sie den Ausweis nicht, während auf diesen zugegriffen wird - Network problems detected, trying to reach server within 30 seconds. - INFO DESKTOP Information message about cancellation process without working network connectivity - Es wurden Netzwerkprobleme erkannt. Die Verbindung wird weiterhin für 30 Sekunden versucht. + Back to start page + LABEL ANDROID IOS + Zur Startseite + + + Back to setup + LABEL ANDROID IOS + Zurück zur Einrichtung Aborting process and informing the service provider @@ -156,19 +219,16 @@ Der Vorgang wird beendet und der Anbieter informiert - Error code: %1 - INFO DESKTOP Error code (string) of current GlobalStatus code, shown as header of popup. - Fehlercode: %1 - - - Back to start page - LABEL ANDROID IOS - Zur Startseite + Network problems detected, trying to reach server within 30 seconds. + INFO DESKTOP Information message about cancellation process without working network connectivity + Es wurden Netzwerkprobleme erkannt. Die Verbindung wird weiterhin für 30 Sekunden versucht. - Back to setup - LABEL ANDROID IOS - Zurück zur Einrichtung + Authentication progress + LABEL DESKTOP Name of an progress indicator during an authentication read by screen readers +---------- +LABEL ANDROID IOS Name of an progress indicator during an authentication read by screen readers + Authentifizierungsfortschritt @@ -182,6 +242,39 @@ + AutoRedirectDecision + + Currently, the app automatically redirects you back to the service provider after authentication. This may not allow your screen reader to provide all information. To ensure you receive all information, enable manual redirection to the service provider. + INFO ALL_PLATFORMS + Aktuell leitet Sie die App nach einer Authentisierung automatisch zurück zum Anbieter. Hierbei gibt Ihr Bildschirmleser möglicherweise nicht alle Informationen wieder. Um zu gewährleisten, dass Sie alle Informationen erhalten, schalten Sie die manuelle Weiterleitung zum Anbieter ein. + + + You can change your preference at any time in the settings. + INFO ALL_PLATFORMS + Sie können diese Option jederzeit in den Einstellungen ändern. + + + Skip and keep automatic redirection + LABEL ALL_PLATFORMS + Überspringen und automatische Weiterleitung behalten + + + Enable manual redirection + INFO ALL_PLATFORMS + Manuelle Weiterleitung einschalten + + + Optimize your settings for screen reading + INFO ALL_PLATFORMS + Optimieren Sie ihre Einstellung für das Bildschirmlesen + + + Do not display this message in future. + LABEL ALL_PLATFORMS + Diesen Hinweis in Zukunft nicht mehr anzeigen. + + + AutostartView Do you want to automatically start the %1 after boot? @@ -223,16 +316,11 @@ - BaseController + BaseHeading - Process finished successfully. You may now remove your ID card from the device. - INFO ALL_PLATFORMS The workflow finished successfully, the ID card may (and should) be removed from the card reader. - Der Vorgang war erfolgreich. Sie können nun Ihren Ausweis vom Gerät entfernen. - - - You may now remove your ID card from the device. - INFO ALL_PLATFORMS The workflow is completed, the ID card may (and should) be removed from the card reader. - Sie können nun Ihren Ausweis vom Gerät entfernen. + Heading + LABEL WINDOWS Screenreader announcement, that the current item is a heading. + Überschrift @@ -333,6 +421,94 @@ + CardNotActivatedBaseView + + Activate ID card with PIN reset letter + LABEL ALL_PLATFORMS + Ausweis mit dem PIN-Rücksetzbrief freischalten + + + Request eID function activation + LABEL ALL_PLATFORMS + Aktivierung der Online-Ausweisfunktion anfordern + + + eID function is not activated + LABEL ALL_PLATFORMS + Online-Ausweisfunktion ist nicht aktiviert + + + Did you recently order a PIN reset letter with an activation code? + LABEL ALL_PLATFORMS + Haben Sie kürzlich einen PIN-Rücksetzbrief mit Aktivierungscode bestellt? + + + Activate your ID card using the activation code + LABEL ALL_PLATFORMS + Schalten Sie Ihren Ausweis mit dem Aktivierungscode frei + + + Yes, I already have an activation code + LABEL ALL_PLATFORMS + Ja, ich habe bereits einen Aktivierungscode + + + Request the activation of the eID function + LABEL ALL_PLATFORMS + Fordern Sie die Aktivierung der Online-Ausweisfunktion an + + + No, I don't have an activation code + LABEL ALL_PLATFORMS + Nein, ich habe keinen Aktivierungscode + + + Abort setup + LABEL ALL_PLATFORMS + Einrichtung abbrechen + + + Enter your activation code of your present PIN reset letter into the following website. + LABEL ALL_PLATFORMS + Geben Sie den Aktivierungscode aus Ihrem vorliegenden PIN-Rücksetzbrief auf der nachfolgenden Webseite ein. + + + Enter activation code + LABEL ALL_PLATFORMS + Aktivierungscode eingeben + + + Request action code + LABEL ALL_PLATFORMS + Aktivierungscode anfordern + + + You can request a PIN reset letter with an activation code on the following website. + LABEL ALL_PLATFORMS + Auf der nachfolgenden Webseite können Sie einen PIN-Rücksetzbrief mit Aktivierungscode anfordern. + + + Online via PIN reset service + LABEL ALL_PLATFORMS + Online per PIN-Rücksetzdienst + + + Find competent authority + LABEL ALL_PLATFORMS + Ausweisbehörde finden + + + You can activate the eID function directly at your competent authority. + LABEL ALL_PLATFORMS + Sie können direkt bei Ihrer zuständigen Ausweisbehörde die Online-Ausweisfunktion aktivieren. + + + At your competent authority + LABEL ALL_PLATFORMS + Bei Ihrer Ausweisbehörde + + + CardPositionView Retry @@ -708,9 +884,14 @@ Was bedeutet das? - You may now try the function: "See my personal data". Press the "%1" button to do so now. - LABEL ALL_PLATFORMS - Probieren Sie zum Abschluss der Prüfung die Funktion "Meine Daten einsehen". Drücken Sie "%1" um fortzufahren. + You may now try the function: "See my personal data". Click the "%1" button to do so now. + LABEL DESKTOP + Probieren Sie zum Abschluss der Prüfung die Funktion "Meine Daten einsehen". Klicken Sie "%1" um fortzufahren. + + + You may now try the function: "See my personal data". Tap the "%1" button to do so now. + LABEL ANDROID IOS + Probieren Sie zum Abschluss der Prüfung die Funktion "Meine Daten einsehen". Tippen Sie "%1" um fortzufahren. NFC supported @@ -836,11 +1017,6 @@ Es war nicht möglich, eine stabile Verbindung mit Ihrem Ausweis herzustellen.<br><br>Bitte starten Sie die Prüfung erneut. Versuchen Sie eine andere Kartenposition und achten Sie darauf, die Karte während der Prüfung nicht zu bewegen.<br><br>Wenn auch bei unterschiedlichen Kartenpositionen keine Verbindung zur Ausweiskarte hergestellt werden kann, deutet dies darauf hin, dass die NFC-Schnittstelle Ihres mobilen Geräts die Ausweiskarte nicht ausreichend mit Strom versorgen kann.<br><br>Mit der %1 kompatible Smartphones finden Sie auf unserer <a href="%2">Website</a>. - Abort setup - LABEL ALL_PLATFORMS - Einrichtung abbrechen - - ID card PIN suspended LABEL ALL_PLATFORMS Karten-PIN pausiert @@ -848,7 +1024,7 @@ The ID card PIN has been entered incorrectly 2 times in a row. This is why you must first enter the 6-digit Card Access Number (CAN) for the next identification process. You can find it at the bottom right of the front of your ID card. LABEL ALL_PLATFORMS Sentence 1 of 3 of CAN explanation - Die Karten-PIN wurde 2 Mal hintereinander falsch eingegeben. Deshalb müssen Sie beim nächsten Ausweisvorgang zunächst die 6-stellig Zugangsnummer (CAN) eingeben. Sie finden diese auf der Vorderseite Ihres Ausweises unten rechts. + Die Karten-PIN wurde 2 Mal hintereinander falsch eingegeben. Deshalb müssen Sie beim nächsten Ausweisvorgang zunächst die 6-stellige Zugangsnummer (CAN) eingeben. Sie finden diese auf der Vorderseite Ihres Ausweises unten rechts. You may now try the function: "See my personal data". @@ -888,17 +1064,7 @@ Es wurde kein unterstützter Ausweis erkannt. Die %1 unterstützt:<p><ul><li>deutsche Personalausweise</li><li>elektronische Aufenthaltstitel (eAT)</li><li>eID-Karten für EU-/EWR-Bürger</li></ul></p>Haben Sie eines der genannten Dokumente verwendet und diese Fehlermeldung erscheint dennoch, starten Sie bitte die Prüfung erneut. - eID function disabled - LABEL ALL_PLATFORMS - Online-Ausweis deaktiviert - - - Activate the eID function. - LABEL ALL_PLATFORMS Hint when a workflow failed because the eID function was not activated - Online-Ausweisfunktion aktivieren - - - You may continue the onboarding and change your PIN. + You may continue the setup and change your PIN. LABEL ALL_PLATFORMS Sentence 2 of 3 of CAN explanation ---------- LABEL ALL_PLATFORMS Sentence 2 of 3 of PUK explanation @@ -1080,24 +1246,19 @@ Vorgang abbrechen - The user interface of the %1 is closed. + How should the %1 be closed in the future? INFO DESKTOP Header of the popup that is shown when the AA is closed for the first time. - Die Benutzeroberfläche der %1 wird geschlossen. + Wie soll die %1 zukünftig beendet werden? - The %1 is closed. - INFO DESKTOP Header of the popup that is shown when the AA is quit for the first time. - Die %1 wird beendet. - - - Completely close the app + Close completely LABEL DESKTOP - App vollständig beenden + Vollständig beenden - Just close the user interface + Leave active in the background LABEL DESKTOP - Nur die Nutzeroberfläche schließen + Im Hintergrund aktiv lassen If the %1 is closed, it is no longer available for authentication. You must then restart the app to authenticate yourself to service providers. @@ -1105,44 +1266,24 @@ Wenn die %1 beendet wird, steht sie nicht länger für eine Authentisierung zur Verfügung. Sie müssen die App dann erneut starten, um sich gegenüber Dienstanbietern auszuweisen. - If the %1 is terminated, it is no longer available for authentication. You must then restart the app in order to identify yourself to service providers. - INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is disabled. - Wenn die %1 beendet wird, steht sie nicht länger für eine Authentisierung zur Verfügung. Sie müssen die App dann erneut starten, um sich gegenüber Dienstanbietern auszuweisen. - - - The app remains available via the icon in the system tray. Click on the %1 icon to reopen the user interface. - INFO DESKTOP Content of the popup that is shown when the AA is closed and the tray icon is enabled. - Die App steht weiterhin im Infobereich zur Verfügung. Klicken Sie auf das Symbol der %1, um die Anwendung wieder zu öffnen. - - - The app remains active in the background and can be reopened via the %1 icon on the menu bar again. - INFO DESKTOP Content of the MacOS-popup that is shown when the AA is closed and the tray icon is enabled. - Die App bleibt im Hintergrund aktiv und kann über das %1-Symbol auf der Menüleiste wieder geöffnet werden. - - - The app remains active in the background and can be reopened via the %1 icon in the notification area of the Windows taskbar. - INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is enabled. - Die App bleibt im Hintergrund aktiv und kann über das %1-Symbol im Infobereich der Windows-Taskleiste wieder geöffnet werden. - - Do not display this message in future. LABEL DESKTOP Diesen Hinweis in Zukunft nicht mehr anzeigen. - If you only close the user interface, the app remains active in the background in the future and can be opened again via the %1 icon on the menu bar. - INFO DESKTOP Content of the MacOS-popup that is shown when the AA is closed and the tray icon is disabled. - Wenn Sie nur die Nutzeroberfläche schließen, bleibt die App zukünftig im Hintergrund aktiv und kann über das %1-Symbol auf der Menüleiste wieder geöffnet werden. + If the %1 remains active in the background, it will open automatically as soon as you start an authentication. You can still open the %1 manually at any time. + INFO DESKTOP %1 is replaced with the application name + Wenn die %1 im Hintergrund aktiv bleibt, wird sie automatisch geöffnet, sobald Sie eine Authentisierung starten. Sie können die AusweisApp auch weiterhin jederzeit manuell öffnen. - If you only close the user interface, the app remains active in the background in the future and can be reopened via the %1 icon in the notification area of the Windows taskbar. - INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is disabled. - Wenn Sie nur die Nutzeroberfläche schließen, bleibt die App zukünftig im Hintergrund aktiv und kann über das AusweisApp-Symbol im Infobereich der Windows-Taskleiste wieder geöffnet werden. + Abort process + INFO DESKTOP + Vorgang abbrechen - Close user interface to menu bar - LABEL DESKTOP - Schließe die Nutzeroberfläche in die Menüleiste + You can change your selection at any time in the settings. + INFO DESKTOP Note to the user that the setting is available in the settings + Sie können Ihre Auswahl jederzeit in den Einstellungen ändern. @@ -1167,7 +1308,7 @@ - DarkModeButtons + DarkModeButtonData System LABEL ALL_PLATFORMS @@ -1257,11 +1398,6 @@ DetachedLogView - Select log: - LABEL DESKTOP - Protokoll auswählen: - - Zoom: LABEL DESKTOP Zoom: @@ -1306,6 +1442,11 @@ LABEL DESKTOP Filter. Deaktiviert. + + Current Log + LABEL DESKTOP + Aktuelles Protokoll + DetachedLogViewWindow @@ -1389,6 +1530,16 @@ LABEL DESKTOP Im Entwicklermodus werden einige Sicherheitsprüfungen abgestellt und die Authentisierung bei bestimmten Fehlern trotzdem fortgesetzt. Übergangene Fehler werden in den Benachrichtigungen angezeigt. Der Entwicklermodus funktioniert nur in der Test-PKI. + + Using the developer mode forces the notifications to be enabled. + LABEL DESKTOP Only visible when the user activates the developer mode in the settings. + Im Entwicklermodus werden die internen Benachrichtigungen erzwungen. + + + Show notifications inside of %1 + LABEL DESKTOP + Benachrichtigungen in der %1 anzeigen + DevicesListDelegate @@ -1715,18 +1866,8 @@ GCollapsible - collapse - LABEL ANDROID IOS - einklappen - - - expand - LABEL ANDROID IOS - ausklappen - - Currently selected is %1 - LABEL ANDROID IOS + LABEL ALL_PLATFORMS Aktuell ausgewählt ist %1 @@ -1741,24 +1882,25 @@ GProgressBar - %1 percent done - %1 Prozent abgeschlossen + Progress + LABEL ALL_PLATFORMS + Fortschritt - GStagedProgressBar + GRadioButton - Step %1 of %2. This step is %3 percent complete. - LABEL ALL_PLATFORMS - Schritt %1 von %2 zu %3 Prozent abgeschlossen. + checked + LABEL DESKTOP + aktiviert - GText + GStagedProgressBar - Press space to open link - INFO DESKTOP Text read by screen reader if the text contains a weblink which may be opened. - Drücken Sie die Leertaste, um diesen Link zu öffnen + Step %1 of %2. This step is %3 percent complete. + LABEL ALL_PLATFORMS + Schritt %1 von %2 zu %3 Prozent abgeschlossen. @@ -1776,11 +1918,6 @@ Verhalten - Using the developer mode forces the notifications to be enabled. - LABEL DESKTOP Only visible when the user activates the developer mode in the settings. - Im Entwicklermodus werden die internen Benachrichtigungen erzwungen. - - Network LABEL DESKTOP Netzwerk @@ -1796,7 +1933,7 @@ Erscheinungsbild - Use the system font + Use system font LABEL DESKTOP Systemschriftart verwenden @@ -1811,11 +1948,6 @@ %1-Fenster nach Authentisierung schließen - Show notifications inside of %1 - LABEL DESKTOP - Benachrichtigungen in der %1 anzeigen - - Change language LABEL DESKTOP Sprache wechseln @@ -1870,6 +2002,31 @@ LABEL WINDOWS Text for attaching the AA to the system tray %1 im Infobereich anheften (empfohlen) + + Automatically check for software updates at program start (recommended) + LABEL DESKTOP + Automatisch nach Software-Aktualisierungen suchen (empfohlen) + + + Show update + LABEL DESKTOP + Aktualisierung anzeigen + + + Start manual search for software update + LABEL DESKTOP + Manuelle Suche nach Software-Aktualisierung starten + + + Abort search + LABEL DESKTOP + Suche abbrechen + + + When you start %1, it automatically checks for updates. Updates are not performed automatically. If this option is disabled, you have to manually check for updates in the settings. + LABEL DESKTOP %1 is replaced with the application name + Beim Starten der %1 wird automatisch nach Updates gesucht. Eine automatische Ausführung der Aktualisierung findet nicht statt. Ist diese Option ausgeschaltet, müssen mögliche Aktualisierungen manuell in den Einstellungen gesucht werden. + GeneralWorkflow @@ -1883,9 +2040,9 @@ Der verwendete Kartenleser erfüllt leider nicht die technischen Voraussetzungen (Extended Length wird nicht unterstützt). - Place ID card + Read out ID card with connected device LABEL DESKTOP - Ausweis auflegen + Ausweis mit verbundenem Gerät auslesen Connect USB card reader or smartphone @@ -2086,6 +2243,19 @@ + LinkQualityAnimation + + Link quality unavailable. + INFO ALL_PLATFORMS + Verbindungsqualität nicht verfügbar. + + + %1% link quality. + INFO ALL_PLATFORMS %1 is replaced with a number between 0 and 100 + %1% Verbindungsqualität. + + + LocalNetworkInfo Go to application settings @@ -2099,15 +2269,45 @@ + LogFilesView + + Select Log + LABEL ANDROID IOS + Protokoll auswählen + + + Delete + LABEL ANDROID IOS + Löschen + + + All old logs will be deleted. + INFO ANDROID IOS All logfiles are about to be removed, user confirmation required. + Alle Protokolle werden gelöscht. + + + Delete all logs + LABEL ANDROID IOS + Alle Protokolle löschen + + + LogTitleBarControls Share log + LABEL ANDROID IOS Protokoll teilen Delete all logs + LABEL ANDROID IOS Alle Protokolle löschen + + Filter + LABEL ANDROID IOS + Filter + LogView @@ -2136,40 +2336,20 @@ All old logs will be deleted. - INFO DESKTOP All logfiles are about to be removed, user confirmation required. ----------- -INFO ANDROID IOS All logfiles are about to be removed, user confirmation required. + INFO DESKTOP All logfiles are about to be removed, user confirmation required. Alle Protokolle werden gelöscht. - Log - LABEL ANDROID IOS - Protokoll - - - Select log from list. - Protokoll aus Liste auswählen. - - Delete all logs - LABEL DESKTOP ----------- -LABEL ANDROID IOS + LABEL DESKTOP Alle Protokolle löschen Delete - LABEL DESKTOP ----------- -LABEL ANDROID IOS + LABEL DESKTOP Löschen - The log entry was copied to the clipboard. - INFO ANDROID IOS Toast message used to confirm the copy of a log entry. - Der Protokolleintrag wurde in die Zwischenablage kopiert. - - Filter LABEL ANDROID IOS Filter @@ -2194,7 +2374,9 @@ LogViewDelegate The log entry was copied to the clipboard. - INFO DESKTOP Toast message used to confirm the copy of a log entry. + INFO DESKTOP Toast message used to confirm the copy of a log entry. +---------- +INFO ANDROID IOS Toast message used to confirm the copy of a log entry. Der Protokolleintrag wurde in die Zwischenablage kopiert. @@ -2275,9 +2457,9 @@ Allgemein - Version information - LABEL ANDROID IOS - Versionsinformationen + %1 version + LABEL ANDROID IOS %1 is replaced with the application name + %1-Version Software license @@ -2354,7 +2536,7 @@ Nutzungsbedingungen und Softwarelizenz - Start onboarding + Start setup LABEL ANDROID IOS Einrichtung starten @@ -2405,12 +2587,12 @@ Kontakt - Start onboarding + Start setup LABEL DESKTOP Einrichtung starten - Onboarding + Setup LABEL DESKTOP Einrichtung @@ -2483,8 +2665,10 @@ Die 5-stellige Transport-PIN ist eine %1Einmal-PIN%2, die Sie bei Beantragung des Online-Ausweises per %1Brief%2 erhalten. - When setting up your ID card, you will %1replace%2 this 5-digit %1Transport PIN with%2 a 6-digit, %1self chosen card PIN%2. - INFO ALL_PLATFORMS Answer to the question 'Where do I find the Transport PIN?' paragraph 2/2 + When you set up your ID card, you will %1replace%2 this 5-digit %1Transport PIN with%2 a 6-digit, %1self chosen card PIN%2. + INFO ALL_PLATFORMS Answer to the question 'Where do I find the Transport PIN?' paragraph 2/2 +---------- +INFO ALL_PLATFORMS Description text explaining the PINs 3/7 Wenn Sie Ihren Online-Ausweis einrichten, %1ersetzen Sie%2 diese 5-stellige %1Transport-PIN durch%2 eine 6-stellige, %1selbstgewählte Karten-PIN%2. @@ -2493,9 +2677,9 @@ Wo finde ich die PUK? - The PUK is a %1 10-digit number%2 that you can find in the %1PIN letter%2 that was sent to you by mail after you %1applied for your ID card%2. You may find it to the %1right%2 of the 5-digit %1Transport PIN%2. + The PUK is a%1 10-digit number%2 that you can find in the %1PIN letter%2 that was sent to you by mail after you %1applied for your ID card%2. You may find it to the %1right%2 of the 5-digit %1Transport PIN%2. LABEL ALL_PLATFORMS INFO ALL_PLATFORMS Answer to the question 'Where do I find the PUK?' - Die PUK ist eine %1 10-stellige Zahl%2, die Sie in dem %1PIN-Brief%2 finden, den Sie %1nach Beantragung Ihres Ausweises%2 erhalten. Sie befindet sich %1rechts neben%2 der 5-stelligen %1Transport-PIN%2. + Die PUK ist eine%1 10-stellige Zahl%2, die Sie in dem %1PIN-Brief%2 finden, den Sie %1nach Beantragung Ihres Ausweises%2 erhalten. Sie befindet sich %1rechts neben%2 der 5-stelligen %1Transport-PIN%2. Why is the PUK required? @@ -2503,9 +2687,9 @@ Warum wird die PUK verlangt? - The PUK is required if the %1card PIN has been entered incorrectly 3 times%2 in a row. As a result, the card PIN is blocked. By entering the PUK you will %1unblock the card PIN%2 and have %1 3 more attempts%2 to enter the correct PIN. + The PUK is required if the %1card PIN has been entered incorrectly 3 times%2 in a row. As a result, the card PIN is blocked. By entering the PUK you will %1unblock the card PIN%2 and have%1 3 more attempts%2 to enter the correct PIN. INFO ALL_PLATFORMS Answer to the question 'Why is the PUK required?' - Die PUK wird verlangt, wenn die %1Karten-PIN 3-mal falsch eingegeben%2 wurde. Die Karten-PIN ist dadurch gesperrt. Durch die Eingabe der PUK %1entsperren Sie die Karten-PIN%2 und haben %1 3 weitere Versuche%2, die richtige PIN einzugeben. + Die PUK wird verlangt, wenn die %1Karten-PIN 3 Mal falsch eingegeben%2 wurde. Die Karten-PIN ist dadurch gesperrt. Durch die Eingabe der PUK %1entsperren Sie die Karten-PIN%2 und haben%1 3 weitere Versuche%2, die richtige PIN einzugeben. My PUK does not work @@ -2645,14 +2829,9 @@ Sie können Ihre 6-stellige Smart-eID-PIN jederzeit und unbegrenzt oft ändern, solange Ihnen Ihre gültige Smart-eID-PIN bekannt ist. - When you set up the eID function, you will %1replace%2 this 5-digit %1Transport PIN%2 with a 6-digit %1card PIN that you choose yourself%2. - INFO ALL_PLATFORMS Description text explaining the PINs 3/7 - Wenn Sie Ihren Online-Ausweis einrichten, %1ersetzen Sie%2 diese 5-stellige %1Transport-PIN durch%2 eine 6-stellige, %1selbstgewählte Karten-PIN%2. - - - The 6-digit card PIN is a %1number that you choose yourself%2 when you set up the eID function for the first time. It %1replaces%2 your %1 5-digit Transport PIN%2. + The 6-digit card PIN is a %1number that you choose yourself%2 when you set up the eID function for the first time. It %1replaces%2 your%1 5-digit Transport PIN%2. INFO ALL_PLATFORMS Description text explaining the PINs 4/7 - Die 6-stellige Karten-PIN ist %1eine Geheimnummer, die Sie selbst wählen%2, wenn Sie Ihren Online-Ausweis zum ersten Mal einrichten. Sie %1ersetzt%2 Ihre %1 5-stellige Transport-PIN%2. + Die 6-stellige Karten-PIN ist %1eine Geheimnummer, die Sie selbst wählen%2, wenn Sie Ihren Online-Ausweis zum ersten Mal einrichten. Sie %1ersetzt%2 Ihre%1 5-stellige Transport-PIN%2. The Smart-eID PIN also has six digits. You also choose that PIN yourself while setting up the Smart-eID for the first time. @@ -2705,9 +2884,9 @@ Ich habe nur eine 5-stellige Transport-PIN - You need to change the %1 5-digit Transport PIN%2 to your personal card PIN. Use %1Change PIN > Transport PIN%2 from the startpage to do so. + You need to change the%1 5-digit Transport PIN%2 to your personal card PIN. Use %1Change PIN > Transport PIN%2 from the startpage to do so. INFO ALL_PLATFORMS Explanation if only the Transport PIN is at hand - Die %1 5-stellige Transport-PIN%2 müssen Sie durch Ihre persönliche Karten-PIN ersetzen. Wählen Sie hierfür auf der Startseite %1PIN ändern > Transport-PIN%2. + Die%1 5-stellige Transport-PIN%2 müssen Sie durch Ihre persönliche Karten-PIN ersetzen. Wählen Sie hierfür auf der Startseite %1PIN ändern > Transport-PIN%2. If you have forgotten your Smart-eID PIN, you can renew your Smart-eID and thereby set a new PIN. @@ -2845,7 +3024,7 @@ You may be using security software that prevents pairing. LABEL ALL_PLATFORMS - Es kann sein, dass Sie Sicherheitssoftware verwenden, die die Kopplung verhindert. + Es kann sein, dass Sie Sicherheitssoftware verwenden, welche die Kopplung verhindert. Updates @@ -2917,12 +3096,19 @@ NavigationAction Cancel + LABEL DESKTOP Abbrechen Back + LABEL DESKTOP Zurück + + Close + LABEL DESKTOP + Schließen + NavigationView @@ -3049,7 +3235,7 @@ NFC scan is not running - INFO ANDROID IOS NFC is available and enabled but needs to be started. + INFO ANDROID IOS Der NFC-Scan ist nicht aktiv @@ -3069,7 +3255,7 @@ Start scan - INFO ANDROID IOS + INFO ANDROID IOS NFC is available and enabled but needs to be started. Scan starten @@ -3154,30 +3340,48 @@ + Notifications + + Notification: %1 + LABEL DESKTOP %1 will be replaced with a notification text + Benachrichtigung: %1 + + + NumberField The number is hidden. - LABEL DESKTOP Screenreader text for the password field + LABEL ALL_PLATFORMS Screenreader text for the password field Die Geheimzahl ist ausgeblendet. You entered %1 of %2 digits. - LABEL DESKTOP Screenreader text for the password field + LABEL ALL_PLATFORMS Screenreader text for the password field Sie haben %1 von %2 Ziffern eingegeben. - Press to hide the number + Click to hide the number LABEL DESKTOP Screenreader text for the eye icon to change the password visibility - Drücken Sie die Taste um die Geheimnummer einzublenden + Klicken Sie die Taste um die Geheimnummer auszublenden - Press to show the number + Tap to hide the number + LABEL ANDROID IOS Screenreader text for the eye icon to change the password visibility + Tippen Sie die Taste um die Geheimnummer auszublenden + + + Click to show the number LABEL DESKTOP Screenreader text for the eye icon to change the password visibility - Drücken Sie die Taste um die Geheimnummer auszublenden + Klicken Sie die Taste um die Geheimnummer einzublenden + + + Tap to show the number + LABEL ANDROID IOS Screenreader text for the eye icon to change the password visibility + Tippen Sie die Taste um die Geheimnummer einzublenden The number is visible. Digits entered so far: %1 - LABEL DESKTOP Screenreader text for the password field + LABEL ALL_PLATFORMS Screenreader text for the password field Die Geheimnummer ist sichtbar. Bisher eingegebene Ziffern: %1 @@ -3331,7 +3535,7 @@ If you have already %1chosen a 6-digit card PIN%2 for this ID card (either in the %3 or at the competent authority) %1or%2 have ordered a %1PIN reset letter%2, this will apply. LABEL ALL_PLATFORMS Description of the confirmation view after the set up AusweisApp stage. %1 + %2 are for bold formatting, %3 is replaced with AusweisApp. - Falls Sie für diesen Ausweis bereits eine %1 6-stellige Karten-PIN gewählt%2 haben (entweder in der %3 oder bei der zuständigen Behörde) %1oder%2 einen %1PIN-Rücksetzbrief bestellt%2 haben, gilt diese. + Falls Sie für diesen Ausweis bereits eine%1 6-stellige Karten-PIN gewählt%2 haben (entweder in der %3 oder bei der zuständigen Behörde) %1oder%2 einen %1PIN-Rücksetzbrief bestellt%2 haben, gilt diese. Otherwise, use your %1Transport PIN%2 that you received by letter when you first applied for the ID card. You then replace this %1one-time PIN%2 with your personal card PIN in the %3. @@ -3425,7 +3629,7 @@ Leider erfüllen Sie nicht alle Anforderungen, um die %1 zu nutzen. - You may restart the setup anytime under %1Help > Onboarding%2. + You may restart the setup anytime under %1Help > Setup%2. LABEL ALL_PLATFORMS %1 and %2 are replaced with bold emphasis. Sie können die Einrichtung jederzeit unter %1Hilfe > Einrichtung%2 erneut ausführen. @@ -3537,7 +3741,7 @@ Then select %1Pair device%2 or, if a device was already paired, %1Pair new device%2. LABEL DESKTOP %1/%2 are replaced with bold highlighting - Wählen Sie dann %1Gerät koppeln%2 oder, falls bereit ein Gerät gekoppelt ist, %1Neues Gerät koppeln%2. + Wählen Sie dann %1Gerät koppeln%2 oder, falls bereits ein Gerät gekoppelt ist, %1Neues Gerät koppeln%2. or @@ -3545,14 +3749,9 @@ oder - Afterwards, a pairing code is displayed on your smartphone. You may then select the device in the list below to enter the pairing code. - LABEL DESKTOP - Anschließend wird ein Kopplungscode auf Ihrem Smartphone angezeigt. Dann können Sie das Gerät in der untenstehenden Liste auswählen, um den Kopplungscode einzugeben. - - - Press space to continue onboarding using the smartphone "%1" - LABEL DESKTOP - Drücken Sie die Leertaste um die Einrichtung mit dem Smartphone \"%1\" fortzusetzen + Afterwards, a pairing code is displayed on your smartphone. You may then select the device in the %1 list to enter the pairing code. + LABEL DESKTOP %1 will be replaced with the Available Devices list name + Anschließend wird ein Kopplungscode auf Ihrem Smartphone angezeigt. Dann können Sie das Gerät in der Liste %1 auswählen, um den Kopplungscode einzugeben. Use device @@ -3630,7 +3829,9 @@ OnboardingView Continue - LABEL DESKTOP + LABEL DESKTOP +---------- +LABEL ANDROID IOS Weiter @@ -3650,24 +3851,34 @@ Open %1 on your %2other device%3. - LABEL ANDROID IOS Assistance text for pairing new devices. Step 1 of 4 + LABEL ANDROID IOS Assistance text for pairing new devices. Step 1 of 5 Öffnen Sie auf Ihrem %2anderen Gerät%3 die %1. - On that device go to %1Settings%2 and then %1Smartphone as card reader%2 resp. %1Manage pairings%2. - LABEL ANDROID IOS Assistance text for pairing new devices. Step 2 of 4. %1 and %2 are surrounding tags for bold font. - Gehen Sie dort in die %1Einstellungen%2 und dann zu %1Smartphone als Kartenleser%2 bzw. %1Kopplungen verwalten%2. + Make sure that both devices are on the %1same network%2 (e.g. WiFi). + LABEL ANDROID IOS Assistance text for pairing new devices. Step 2 of 5. %1 and %2 are surrounding tags for bold font. + Stellen Sie sicher, dass beide Geräte mit %1demselben Netzwerk%2, z.B. per WLAN verbunden sind. + + + On your other device, go to %1Settings%2 and then %1Smartphone as card reader%2 resp. %1Manage pairings%2. + LABEL ANDROID IOS Assistance text for pairing new devices. Step 3 of 5. %1 and %2 are surrounding tags for bold font. + Gehen Sie auf Ihrem anderen Gerät in die %1Einstellungen%2 und dann zu %1Smartphone als Kartenleser%2 bzw. %1Kopplungen verwalten%2. Choose this smartphone in the list to pair it. - LABEL ANDROID IOS Assistance text for pairing new devices. Step 3 of 4 + LABEL ANDROID IOS Assistance text for pairing new devices. Step 4 of 5 Wählen Sie in der angezeigten Liste dieses Smartphone aus, um es zu koppeln. Enter the pairing code "%1". - LABEL ANDROID IOS Provide pairing code. Step 4 of 4 + LABEL ANDROID IOS Provide pairing code. Step 5 of 5 Geben Sie den Kopplungscode "%1" ein. + + Please follow these steps: + LABEL ANDROID IOS + Bitte folgen Sie diesen Schritten: + PairingFailedView @@ -3956,18 +4167,23 @@ PkiSwitch - %1 more presses to toggle the environment (prod/test) for integrated functions. - INFO ANDROID IOS Used in notifications when the user taps the icon + %1 more clicks to toggle the environment (prod/test) for integrated functions. + INFO DESKTOP Used in notifications when the user taps the icon %1 weitere Klicks um die Umgebung (prod/test) für die integrierten Funktionen umzuschalten. - Testmode for the integrated functions activated. + %1 more taps to toggle the environment (prod/test) for integrated functions. INFO ANDROID IOS Used in notifications when the user taps the icon + %1 weitere Tipps um die Umgebung (prod/test) für die integrierten Funktionen umzuschalten. + + + Testmode for the integrated functions activated. + INFO ALL_PLATFORMS Used in notifications when the user taps the icon Testmodus für die integrierten Funktionen aktiviert. Testmode for the integrated functions deactivated. - INFO ANDROID IOS Used in notifications when the user taps the icon + INFO ALL_PLATFORMS Used in notifications when the user taps the icon Testmodus für die integrierten Funktionen deaktiviert. @@ -4037,11 +4253,14 @@ Sie können Ihr %2Smartphone mit NFC Funktion%3 als Kartenleser nutzen. Dafür müssen Sie die %1 auch auf dem Smartphone installieren. Alternativ können Sie einen %2USB-Kartenleser an den PC anschließen%3. - You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a %1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, click here: - LABEL DESKTOP %1 + %2 = Bold Tags, %3 = AusweisApp ----------- -LABEL ANDROID IOS %1 + %2 = Bold Tags, %3 = AusweisApp - Sie haben eine Einmal-PIN, die %1Transport-PIN%2, als Brief von Ihrer Ausweisbehörde erhalten. Diese ersetzen Sie in der %3 oder im Bürgeramt durch eine %1 6-stellige Karten-PIN%2. Wenn Ihnen keine PIN vorliegt oder Sie sich nicht an Ihre Karten-PIN erinnern, klicken Sie hier: + You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a%1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, click here: + LABEL DESKTOP %1 + %2 = Bold Tags, %3 = AusweisApp + Sie haben eine Einmal-PIN, die %1Transport-PIN%2, als Brief von Ihrer Ausweisbehörde erhalten. Diese ersetzen Sie in der %3 oder im Bürgeramt durch eine%1 6-stellige Karten-PIN%2. Wenn Ihnen keine PIN vorliegt oder Sie sich nicht an Ihre Karten-PIN erinnern, klicken Sie hier: + + + You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a%1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, tap here: + LABEL ANDROID IOS %1 + %2 = Bold Tags, %3 = AusweisApp + Sie haben eine Einmal-PIN, die %1Transport-PIN%2, als Brief von Ihrer Ausweisbehörde erhalten. Diese ersetzen Sie in der %3 oder im Bürgeramt durch eine%1 6-stellige Karten-PIN%2. Wenn Ihnen keine PIN vorliegt oder Sie sich nicht an Ihre Karten-PIN erinnern, tippen Sie hier: The chip in your ID card is read using %1NFC%2. To do this, simply place the ID card on the %1back of the smartphone%2. @@ -4097,9 +4316,9 @@ Es erfolgt keine Speicherung oder Weiterverarbeitung Ihrer persönlichen Daten. Näheres dazu erfahren Sie in unserer %1. - data privacy statement of the Federal Ministry of the Interior and Community + data privacy statement of the Federal Ministry of the Interior LABEL ALL_PLATFORMS Text of the Smart-eID html link inside of a sentence - Datenschutzerklärung des Bundesministeriums des Innern und für Heimat + Datenschutzerklärung des Bundesministeriums des Innern data privacy statement @@ -4130,7 +4349,7 @@ Anbieter - Touch for more details + Tap for more details LABEL ANDROID IOS Tippen Sie hier für mehr Details @@ -4216,6 +4435,24 @@ + ReaderFoundConfirmation + + Found new smartphone as card reader that is suitable for the ID card. The workflow may now be continued. + LABEL DESKTOP + Es wurde ein neues Smartphone als Kartenleser gefunden, das für den Ausweis geeignet ist. Der Vorgang kann nun fortgesetzt werden. + + + Found new USB card reader that is suitable for the ID card. The workflow may now be continued. + LABEL DESKTOP + Es wurde ein neuer USB-Kartenleser gefunden, der für den Ausweis geeignet ist. Der Vorgang kann nun fortgesetzt werden. + + + Continue + LABEL DESKTOP + Weiter + + + RedirectView Remove the ID card from the card reader @@ -4233,14 +4470,24 @@ Bei Rückfragen oder auftretenden Fehlern zum Vorgang wenden Sie sich bitte an den jeweiligen Anbieter. - You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, click on the "%1" button. - INFO ALL_PLATFORMS Redirect information when automatic redirect is enabled - Die automatische Weiterleitung zum Anbieter erfolgt in wenigen Sekunden. Falls Sie nicht automatisch weitergeleitet werden, drücken Sie auf den Button "%1". + You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, click the "%1" button. + INFO DESKTOP Redirect information when automatic redirect is enabled + Die automatische Weiterleitung zum Anbieter erfolgt in wenigen Sekunden. Falls Sie nicht automatisch weitergeleitet werden, klicken Sie auf den Button "%1". - Press the button to complete the authentication and return to the provider. - INFO ALL_PLATFORMS Redirect information when automatic redirect is disabled - Drücken Sie auf den Button, um die Authentisierung abzuschließen und zum Anbieter zurückzukehren. + Click the button to complete the authentication and return to the provider. + INFO DESKTOP Redirect information when automatic redirect is disabled + Klicken Sie auf den Button, um die Authentisierung abzuschließen und zum Anbieter zurückzukehren. + + + You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, tap the "%1" button. + INFO ANDROID IOS Redirect information when automatic redirect is enabled + Die automatische Weiterleitung zum Anbieter erfolgt in wenigen Sekunden. Falls Sie nicht automatisch weitergeleitet werden, tippen Sie auf den Button "%1". + + + Tap the button to complete the authentication and return to the provider. + INFO ANDROID IOS Redirect information when automatic redirect is disabled + Tippen Sie auf den Button, um die Authentisierung abzuschließen und zum Anbieter zurückzukehren. Return to provider @@ -4271,28 +4518,29 @@ RemoteReaderDelegate - Smartphone named "%1". %2. - INFO DESKTOP Name and status of remote device. %1 is replaced with the name, %2 with the status - Smartphone namens "%1". %2. + Smartphone named "%1". + INFO DESKTOP Name of remote device. %1 is replaced with the name. + Smartphone namens "%1". - Press space to unpair the smartphone "%1". - INFO DESKTOP Text for activation action if the device is paired. - Drücken Sie die Leertaste um das Smartphone "%1" zu entkoppeln. + Status: "%1". + INFO DESKTOP Status of remote device. %1 is replaced with the status. + Status: "%1". - Press space to pair the smartphone "%1". - INFO DESKTOP Text for activation action if the device is unpaired. - Drücken Sie die Leertaste um das Smartphone "%1" zu koppeln. + Pair + LABEL DESKTOP + Koppeln - Remove remote device - Entferne das Gerät + Unpair + LABEL DESKTOP + Entkoppeln - Pair - LABEL DESKTOP - Koppeln + %1 device "%2" + LABEL DESKTOP Text of pairing button, %1 will be Pair/Unpair and %2 is replaced with device name + Gerät "%2" %1 @@ -4349,9 +4597,9 @@ Zuletzt verbunden - Click to remove device + Tap to remove device LABEL ANDROID IOS - Klicken, um das Gerät zu entfernen + Tippen, um das Gerät zu entfernen Remove @@ -4374,9 +4622,9 @@ Kopplung hinzufügen - Click to pair + Tap to pair LABEL ANDROID IOS - Klicken zum Koppeln + Tippen zum Koppeln Please connect your WiFi to use another smartphone as card reader (SaC). @@ -4399,9 +4647,9 @@ Das Gerät wird gekoppelt ... - Click to use device + Tap to use device LABEL ANDROID IOS - Klicken, um das Gerät zu nutzen + Tippen, um das Gerät zu nutzen @@ -4432,11 +4680,6 @@ Starten Sie hierzu auf einem gekoppelten Gerät einen Vorgang. - Pairing code: <b>%1</b> - LABEL ANDROID IOS - Kopplungscode: <b>%1</b> - - Enable WiFi LABEL ANDROID IOS WLAN aktivieren @@ -4488,7 +4731,7 @@ Start pairing of a new device LABEL ANDROID IOS - Kopplung mit einem neuen Gerät starten. + Kopplung mit einem neuen Gerät starten Where do I enter the pairing code? @@ -4496,23 +4739,13 @@ Wo gebe ich den Kopplungscode ein? - Enter the pairing code "%1" in the %2 on your other device. - INFO ANDROID IOS - Geben Sie den Kopplungscode "%1" in der %2 auf Ihrem anderen Gerät ein. - - Cancel pairing LABEL ANDROID IOS Kopplung abbrechen - Activate the card reader, this allows the paired devices to use this smartphone as a card reader. - INFO ANDROID IOS - Aktivieren Sie den Kartenleser, dann können die gekoppelten Geräte dieses Smartphone als Kartenleser nutzen. - - Paired devices may use this Smartphone as a card reader now. - INFO ANDROID IOS + LABEL ANDROID IOS Gekoppelte Geräte können dieses Smartphone jetzt als Kartenleser nutzen. @@ -4525,14 +4758,56 @@ LABEL ANDROID IOS Kartenleser beenden - - - RemoteServiceWifiInfo + + No device paired + LABEL ANDROID IOS + Kein Gerät gekoppelt + + + Card reader not active + LABEL ANDROID IOS + Kartenleser nicht aktiv + + + Use this smartphone as a card reader for a paired device + LABEL ANDROID IOS + Nutzen Sie das Smartphone als Kartenleser für ein gekoppeltes Gerät + + + Activate the card reader + LABEL ANDROID IOS + Aktivieren Sie den Kartenleser + Both devices have to be on the same network (e.g. WiFi). INFO ANDROID IOS The remote service is active. Hint that both devices need to be connected to the same network. Beide Geräte müssen hierfür im selben Netzwerk (z.B. WLAN) sein. + + Pairing code: + LABEL ANDROID IOS + Kopplungscode: + + + This allows the paired devices to use this smartphone as a card reader. + INFO ANDROID IOS + Dann können die gekoppelten Geräte dieses Smartphone als Kartenleser nutzen. + + + Enter the pairing code + LABEL ANDROID IOS + Eingabe des Kopplungscodes + + + Enter the pairing code "%1" in the %2 on your other device. Both devices have to be on the same network (e.g. WiFi). + INFO ANDROID IOS %1 is replaced with the pairing code, %2 with the name "AusweisApp" + Geben Sie den Kopplungscode "%1" in der %2 auf Ihrem anderen Gerät ein. Beide Geräte müssen hierfür im selben Netzwerk (z.B. WLAN) sein. + + + Pairing progress + LABEL ANDROID IOS Name of an progress indicator during the pairing process read by screen readers + Kopplungsfortschritt + RemoteWorkflow @@ -4585,11 +4860,6 @@ ResultErrorView - Error code: - LABEL ANDROID IOS - Fehlercode: - - Show Details LABEL ANDROID IOS Details anzeigen @@ -4638,46 +4908,6 @@ Ziffernblock - Software updates - LABEL DESKTOP - Software-Aktualisierungen - - - Check for updates at program start - LABEL DESKTOP - Automatisch bei Programmstart nach Updates suchen - - - Show update - LABEL DESKTOP - Aktualisierung anzeigen - - - Check now - LABEL DESKTOP - Jetzt überprüfen - - - An update is available (version %1)! - LABEL DESKTOP An update is available, the new version is supplied to the user. - Eine Aktualisierung ist verfügbar (Version %1)! - - - An update is available but retrieving the information failed. - LABEL DESKTOP The updater found an update but not all required update information are valid, this should be a very rare case. - Eine Aktualisierung ist verfügbar, das Herunterladen der Informationen ist jedoch fehlgeschlagen. - - - Your version %1 of %2 is up to date. - LABEL DESKTOP The current version is up to date, no user action is required. - Ihre Version %1 der %2 ist aktuell. - - - No update information available, please check for update manually. - LABEL DESKTOP The automatic update check is disabled (or no network connection was present during app start), a manual check for update is required. - Keine Aktualisierungsinformationen vorhanden, bitte prüfen Sie manuell auf verfügbare Aktualisierungen. - - Shuffle keys LABEL DESKTOP Tasten zufällig anordnen @@ -4729,16 +4959,6 @@ SelfAuthenticationData - Read self-authentication data - LABEL DESKTOP Title of the self authentication result data view - Ausgelesene Daten der Selbstauskunft - - - Successfully read data - INFO DESKTOP Status message that the self authentication successfully completed. - Lesevorgang erfolgreich - - Read data LABEL DESKTOP Title of the self authentication result data view Ausgelesene Daten @@ -4755,6 +4975,19 @@ + SelfAuthenticationHeader + + Successfully read data. + INFO ALL_PLATFORMS Status message that the self authentication successfully completed (1/2). + Lesevorgang erfolgreich. + + + You may now remove your ID card from the device. + INFO ALL_PLATFORMS Status message that the self authentication successfully completed (2/2). + Sie können nun Ihren Ausweis vom Gerät entfernen. + + + SettingsView Settings @@ -4780,7 +5013,7 @@ USB card reader LABEL DESKTOP - USB Kartenleser + USB-Kartenleser Security and privacy @@ -5020,7 +5253,7 @@ Befolgen der Anweisungen auf Ihrem zweiten Gerät - Now follow the instruction for the setup on your PC/Mac. If the onboarding does not start automatically, you may find it under Help > Onboarding. + Now follow the instruction for the setup on your PC/Mac. If the setup does not start automatically, you may find it under Help > Setup. LABEL ANDROID IOS Folgen Sie nun den Anweisungen auf Ihrem PC/Mac. Sollte sich die Einrichtung nicht automatisch öffnen, können Sie diese unter Hilfe > Einrichtung finden. @@ -5373,9 +5606,9 @@ Smart-eID nicht einsatzbereit - Your Smart-eID is ready for use, press "Continue" to proceed. + Your Smart-eID is ready for use, tap "Continue" to proceed. LABEL ANDROID IOS - Ihre Smart-eID ist einsatzbereit. Klicken Sie auf "Weiter", um fortzufahren. + Ihre Smart-eID ist einsatzbereit. Tippen Sie auf "Weiter", um fortzufahren. Please wait a moment. @@ -5465,6 +5698,19 @@ + TabbedPane + + Content of tab "%1" + LABEL DESKTOP %1 will be replaced with the title of the tab + Inhalt der Registerkarte "%1" + + + Sidebar + LABEL DESKTOP + Seitenleiste + + + TabbedPaneDelegate %1 of %2 @@ -5476,11 +5722,16 @@ LABEL DESKTOP Registerkarte ausgewählt + + Tab + LABEL DESKTOP + Registerkarte + TabbedReaderView - Card Readers + Card readers LABEL DESKTOP Kartenleser @@ -5490,11 +5741,7 @@ USB card reader - USB Kartenleser - - - Found new USB card reader that is suitable for the ID card. The workflow may now be continued. - Es wurde ein neuer USB-Kartenleser gefunden, der für den Ausweis geeignet ist. Der Vorgang kann nun fortgesetzt werden. + USB-Kartenleser @@ -5550,15 +5797,14 @@ Internen Benachrichtigungsdialog der %1 einblenden - Title bar - LABEL DESKTOP - Titelleiste - - Hide in-app notifications of %1 LABEL DESKTOP Internen Benachrichtigungsdialog der %1 ausblenden + + Update available + Aktualisierung verfügbar + TitleBarNavigation @@ -5572,18 +5818,23 @@ LABEL ANDROID IOS Zurück + + Close + LABEL ANDROID IOS + Schließen + TrayIconView Yes, attach app to menu bar INFO MACOS Button to decide to attach app to the menu bar on macOS - Ja, App im Benachrichtigungsbereich anheften + Ja, App in der Menüleiste anheften Yes, attach app to tray INFO WINDOWS Button to decide to attach app to the tray on Windows - Ja, App im Benachrichtigungsbereich anheften + Ja, App im Infobereich anheften The %1 continues to run in the background after the application window is closed, so that it can be automatically opened on an authentication. @@ -5603,7 +5854,7 @@ Attaching the app to the tray is therefore recommended. INFO WINDOWS Information text why attaching the app to the tray/menu bar is advisable 3/3 - Daher wird es empfohlen, sie im Benachrichtigungsbereich der Windows-Taskleiste anzuheften + Daher wird es empfohlen, sie im Benachrichtigungsbereich der Windows-Taskleiste anzuheften. No, don't attach app to menu bar @@ -5634,23 +5885,9 @@ Anwendungsaktualisierung - An update information for your platform is not available. - LABEL DESKTOP Resulttext if no update information is available for the current platform. - Für Ihre Plattform sind keine Aktualisierungsinformationen verfügbar. - - - The update information could not be retrieved. Please check your network connection. - LABEL DESKTOP Resulttext if the update information are invalid, might be caused by network issues. - Die Aktualisierungeninformationen konnten nicht heruntergeladen werden. Bitte überprüfen Sie die Netzwerkverbindung. - - - Your version %1 of %2 is up to date! - LABEL DESKTOP The currently installed version is the most recent one, no action is required. - Ihre Version %1 der %2 ist auf dem aktuellen Stand! - - - An update is available (installed version %1) - Eine Aktualisierung ist verfügbar (installierte Version %1) + An update for the outdated installed version (%1) is available for download. + LABEL DESKTOP %1 is replaced with the current version number + Eine Aktualisierung der veralteten installierten Version (%1) ist zum Download verfügbar. Warning - Your operating system is no longer supported @@ -5672,6 +5909,11 @@ INFO DESKTOP Header of the popup that is shown when the app download failed. Warnung - Die Aktualisierung ist fehlgeschlagen + + Update available + LABEL DESKTOP + Aktualisierung verfügbar + UpdateViewButtonRow @@ -5685,33 +5927,56 @@ LABEL DESKTOP Start to download the update and execute it on Windows Aktualisierung starten + + Download progress + LABEL DESKTOP Name of an progress indicator during a download read by screen readers + Dowloadfortschritt + + + The update (version %1) is being performed... + LABEL DESKTOP %1 is replaced with the version number of the software update. + Die Aktualisierung (Version %1) wird durchgeführt... + UpdateViewInformation - New version: + New version LABEL DESKTOP Information about the available, new version number. - Neue Version: + Neue Version - Release date: + Release date LABEL DESKTOP Date when the available update was released. - Erscheinungsdatum: + Erscheinungsdatum - Download size: + Download size LABEL DESKTOP Download size of the available update in megabyte. - Größe der Aktualisierung: + Größe der Aktualisierung - Download link: + Download link LABEL DESKTOP Plaintext link to the update download. - Link zum Download: + Link zum Download - Checksum link: + Checksum link LABEL DESKTOP Link to download checksum to verify the downloaded update file. - Link zur Prüfsumme: + Link zur Prüfsumme + + + + Utils + + Tap to open the following website in your browser: %1 + INFO ANDROID IOS Hint that a link is present, which will open in the browser + Tippen um die Webseite in Ihrem Browser zu öffnen: %1 + + + Press space to open the following website in your browser: %1 + INFO DESKTOP Hint that a link is present, which will open in the browser + Drücken Sie die Leertaste um die Webseite in Ihrem Browser zu öffnen: %1 @@ -5727,14 +5992,9 @@ Entwickleroptionen deaktiviert. - Version Information - LABEL ANDROID IOS - Versionsinformationen - - - %1 more presses to toggle the advanced settings. - INFO ANDROID IOS Used in notifications when the user taps the version information - %1 weitere Klicks um die erweiterten Einstellungen umzuschalten. + %1 version + LABEL ANDROID IOS %1 is replaced with the application name + %1-Version Advanced settings activated. @@ -5761,6 +6021,11 @@ LABEL DESKTOP Barrierefreiheitserklärung + + %1 more taps to toggle the advanced settings. + INFO ANDROID IOS Used in notifications when the user taps the version information + %1 weitere Tipps um die erweiterten Einstellungen umzuschalten. + WhiteListSurveyView @@ -5974,6 +6239,36 @@ INFO DESKTOP Text of the popup that is shown when the execution of the update failed (2/2). Wenn dies nicht hilft, kontaktieren Sie unseren %1Support%2. + + Searching for software updates... + LABEL DESKTOP + Die Suche nach Software-Aktualisierungen wird durchgeführt... + + + An update is available (version %1). + LABEL DESKTOP An update is available, the new version is supplied to the user. + Eine Aktualisierung ist verfügbar (Version %1). + + + Your version %1 of %2 is up to date. + LABEL DESKTOP %1 is replaced with the version number of the software and %2 is replaced with the application name. + Ihre Version %1 der %2 ist aktuell. + + + An update information for your platform is not available. + LABEL DESKTOP + Für Ihre Plattform sind keine Aktualisierungsinformationen verfügbar. + + + An update is available but retrieving the information failed. + LABEL DESKTOP The updater found an update but not all required update information are valid, this should be a very rare case. + Eine Aktualisierung ist verfügbar, das Herunterladen der Informationen ist jedoch fehlgeschlagen. + + + The update information could not be retrieved. Please check your network connection. + LABEL DESKTOP + Die Aktualisierungeninformationen konnten nicht heruntergeladen werden. Bitte überprüfen Sie die Netzwerkverbindung. + governikus::ApplicationModel @@ -6022,6 +6317,11 @@ LABEL ALL_PLATFORMS Zur Startseite + + Connection to ID card lost + LABEL ALL_PLATFORMS + Verbindung zum Ausweis unterbrochen + governikus::CardInfo @@ -6076,7 +6376,7 @@ Angabe der für den Anbieter zuständigen Datenschutzaufsicht - Provider information + Provider Information LABEL ALL_PLATFORMS Anbieterinformationen @@ -6600,9 +6900,9 @@ Der Ausweisvorgang kann nicht gestartet werden. Ein anderer Vorgang ist bereits aktiv. - The connection to the ID card has been lost. The process was aborted. + Restart the authentication process and make sure that the position of the ID card does not change during the reading process. ERROR ALL_PLATFORMS The card was removed after the PACE channel was established. - Die Verbindung zum Ausweis wurde unterbrochen. Der Vorgang wird abgebrochen. + Starten Sie den Authentisierungsvorgang erneut und stellen Sie sicher, dass sich die Position des Ausweises während des Auslesevorgangs nicht verändert. The authenticity of your ID card could not be confirmed. @@ -6875,11 +7175,6 @@ Die Version Ihres Smartphones als Kartenleser (SaK) ist inkompatibel. Installieren Sie bitte auf Ihrem Smartphone und Ihrem Computer die aktuellste %1 Version. - A timeout occurred while trying to establish a connection to the smartphone as card reader (SaC). - ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) timed out. - Bei der Verbindung zum Smartphone als Kartenleser (SaK) kam es zu einer Zeitüberschreitung. - - An error occurred while trying to establish a connection to the smartphone as card reader (SaC). 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 Smartphone als Kartenleser (SaK) ist ein Fehler aufgetreten. @@ -7004,7 +7299,7 @@ - governikus::LogModel + governikus::LogFilesModel Current log LABEL ALL_PLATFORMS @@ -7015,6 +7310,9 @@ LABEL ALL_PLATFORMS Datetime format according to https://doc.qt.io/qt/qdate.html#toString and https://doc.qt.io/qt/qtime.html#toString dd.MM.yyyy hh:mm:ss + + + governikus::LogModel The logfile is disabled. Die Protokolldatei ist deaktiviert. @@ -7087,14 +7385,14 @@ governikus::NumberModel - You have entered an incorrect, 6-digit Smart-eID PIN. You have <b>2 further attempts</b> to enter the correct Smart-eID PIN. - INFO ALL_PLATFORMS The wrong Smart-eID PIN was entered on the first attempt. - Sie haben eine falsche, 6-stellige Smart-eID-PIN eingegeben. Sie haben <b>2 weitere Versuche</b>, die richtige Smart-eID-PIN einzugeben. + You have entered an incorrect, 6-digit Smart-eID PIN. You have%1 2 further attempts%2 to enter the correct Smart-eID PIN. + INFO ALL_PLATFORMS The wrong Smart-eID PIN was entered on the first attempt. %1 + %2 are used to emphasize. + Sie haben eine falsche, 6-stellige Smart-eID-PIN eingegeben. Sie haben%1 2 weitere Versuche%2, die richtige Smart-eID-PIN einzugeben. - You have entered an incorrect, 5-digit Transport PIN 3 times, your <b>Transport PIN is now blocked</b>. To remove the block, the <b>10-digit PUK</b> must be entered first. - INFO ALL_PLATFORMS The Transport PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. - Sie haben 3 Mal eine falsche, 5-stellige Transport-PIN eingegeben, Ihre <b>Transport-PIN ist nun gesperrt</b>. Um die Sperre aufzuheben, muss zunächst die <b>10-stellige PUK</b> eingegeben werden. + You have entered an incorrect, 5-digit Transport PIN 3 times, your %1Transport PIN is now blocked%2. To remove the block, the%1 10-digit PUK%2 must be entered first. + INFO ALL_PLATFORMS The Transport PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. %1 + %2 are used to emphasize. + Sie haben 3 Mal eine falsche, 5-stellige Transport-PIN eingegeben, Ihre %1Transport-PIN ist nun gesperrt%2. Um die Sperre aufzuheben, muss zunächst die%1 10-stellige PUK%2 eingegeben werden. You have entered an incorrect, 6-digit Smart-eID PIN 3 times. Your Smart-eID is now invalidated. To use a Smart-eID again you have to set one up in the guided setup on the start page. @@ -7107,9 +7405,9 @@ Sie haben eine falsche, 10-stellige PUK eingegeben. Bitte versuchen Sie es erneut. - You have entered an <b>incorrect, 6-digit Smart-eID PIN 2 times</b>. After the next failed attempt you will no longer be able to use your Smart-eID and will need to set it up again. - INFO ANDROID IOS The wrong Smart-eID PIN was entered twice, a 3rd wrong attempt could invalidate the Smart-eID. - Sie haben <b>2 Mal eine falsche, 6-stellige Smart-eID-PIN</b> eingegeben. Nach dem nächsten Fehlversuch können Sie Ihre Smart-eID nicht mehr einsetzen und müssen diese neu einrichten. + You have entered an %1incorrect, 6-digit Smart-eID PIN 2 times%2. After the next failed attempt you will no longer be able to use your Smart-eID and will need to set it up again. + INFO ANDROID IOS The wrong Smart-eID PIN was entered twice, a 3rd wrong attempt could invalidate the Smart-eID. %1 + %2 are used to emphasize. + Sie haben%1 2 Mal eine falsche, 6-stellige Smart-eID-PIN%2 eingegeben. Nach dem nächsten Fehlversuch können Sie Ihre Smart-eID nicht mehr einsetzen und müssen diese neu einrichten. The input does not match. Please choose a new Smart-eID PIN. @@ -7126,36 +7424,36 @@ Sie haben eine falsche 6-stellige Karten-PIN eingegeben. - You have <b>2 further attempts</b> to enter the correct ID card PIN. - INFO ALL_PLATFORMS The wrong ID card PIN was entered on the first attempt. Part 2/2 - Sie haben <b>2 weitere Versuche</b>, die richtige Karten-PIN einzugegeben. + You have%1 2 further attempts%2 to enter the correct ID card PIN. + INFO ALL_PLATFORMS The wrong ID card PIN was entered on the first attempt. %1 + %2 are used to emphasize. Part 2/2 + Sie haben%1 2 weitere Versuche%2, die richtige Karten-PIN einzugegeben. - You have entered an <b>incorrect, 6-digit ID card PIN 2 times</b>. - INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. Part 1/2 - Sie haben zum <b>2. Mal eine falsche 6-stellige Karten-PIN</b> eingegeben. + You have entered an %1incorrect, 6-digit ID card PIN 2 times%2. + INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 1/2 + Sie haben zum%1 2. Mal eine falsche 6-stellige Karten-PIN%2 eingegeben. - For a 3rd attempt, the <b>6-digit Card Access Number (CAN)</b> must be entered first. You can find your CAN in the <b>bottom right on the front of your ID card</b>. - INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. Part 2/2 + For a 3rd attempt, the%1 6-digit Card Access Number (CAN)%2 must be entered first. You can find your CAN in the %1bottom right on the front of your ID card%2. + INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 2/2 ---------- -INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. Part 2/2 - Für den 3. Versuch muss zunächst die <b>6-stellige Zugangsnummer (CAN)</b> eingegeben werden. Sie finden Ihre CAN <b>unten rechts auf der Vorderseite Ihres Ausweises</b>. +INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 2/2 + Für den 3. Versuch muss zunächst die%1 6-stellige Zugangsnummer (CAN)%2 eingegeben werden. Sie finden Ihre CAN %1unten rechts auf der Vorderseite Ihres Ausweises%2. - You have entered an incorrect, 6-digit ID card PIN 3 times. Your <b>ID card PIN is now blocked</b>. + You have entered an incorrect, 6-digit ID card PIN 3 times. Your %1ID card PIN is now blocked%2. INFO ALL_PLATFORMS The ID card PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. Part 1/2 - Sie haben 3-mal eine falsche, 6-stellige Karten-PIN eingegeben. Ihre <b>Karten-PIN ist nun gesperrt</b>. + Sie haben 3 Mal eine falsche, 6-stellige Karten-PIN eingegeben. Ihre %1Karten-PIN ist nun gesperrt%2. - To remove the block, the <b>10-digit PUK</b> must be entered first. You can find the PUK in the bottom <b>right next</b> to the Transport PIN in the <b>authority's letter</b>. + To remove the block, the%1 10-digit PUK%2 must be entered first. You can find the PUK in the bottom %1right next%2 to the Transport PIN in the %1authority's letter%2. INFO ALL_PLATFORMS The ID card PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. Part 2/2 - Um die Sperre aufzuheben, muss zunächst die <b>10-stellige PUK</b> eingegeben werden. Sie finden die PUK <b>im Brief der Ausweisbehörde unten rechts</b> neben der Transport-PIN. + Um die Sperre aufzuheben, muss zunächst die%1 10-stellige PUK%2 eingegeben werden. Sie finden die PUK %1im Brief der Ausweisbehörde unten rechts%2 neben der Transport-PIN. - You have entered an <b>incorrect Card Access Number (CAN)</b>. Please try again. You can find your CAN in the <b>bottom right on the front of your ID card</b>. - INFO ALL_PLATFORMS The CAN was entered wrongfully and needs to be supplied again. - Sie haben eine <b>falsche Zugangsnummer (CAN)</b> eingegeben. Bitte versuchen Sie es erneut. Sie finden Ihre CAN <b>unten rechts auf der Vorderseite Ihres Ausweises</b>. + You have entered an %1incorrect Card Access Number (CAN)%2. Please try again. You can find your CAN in the %1bottom right on the front of your ID card%2. + INFO ALL_PLATFORMS The CAN was entered wrongfully and needs to be supplied again. %1 + %2 are used to emphasize. + Sie haben eine %1falsche Zugangsnummer (CAN)%2 eingegeben. Bitte versuchen Sie es erneut. Sie finden Ihre CAN %1unten rechts auf der Vorderseite Ihres Ausweises%2. You have entered an incorrect, 5-digit Transport PIN. @@ -7163,34 +7461,34 @@ Sie haben eine falsche, 5-stellige Transport-PIN eingegeben. - You have <b>2 further attempts</b> to enter the correct Transport PIN. The 5-digit Transport PIN may be found on the <b>bottom left of your PIN letter</b>. - INFO ALL_PLATFORMS The wrong Transport PIN was entered on the first attempt.Part 2/2 - Sie haben <b>2 weitere Versuche</b>, die korrekte Transport-PIN einzugeben. Die 5-stellige Transport-PIN befindet sich <b>unten links in Ihrem PIN-Brief</b>. + You have%1 2 further attempts%2 to enter the correct Transport PIN. The 5-digit Transport PIN may be found on the %1bottom left of your PIN letter%2. + INFO ALL_PLATFORMS The wrong Transport PIN was entered on the first attempt. %1 + %2 are used to emphasize. Part 2/2 + Sie haben%1 2 weitere Versuche%2, die korrekte Transport-PIN einzugeben. Die 5-stellige Transport-PIN befindet sich %1unten links in Ihrem PIN-Brief%2. - You have entered an <b>incorrect, 5-digit Transport PIN 2 times</b>. - INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. Part 1/2 - Sie haben <b>2-mal eine falsche, 5-stellige Transport-PIN</b> eingegeben. + You have entered an %1incorrect, 5-digit Transport PIN 2 times%2. + INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 1/2 + Sie haben%1 2 Mal eine falsche, 5-stellige Transport-PIN%2 eingegeben. - <b>An incorrect PIN has been entered 2 times</b> at the last use of your ID card. - INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. Part 1/2 - Bei der letzten Nutzung ihres Online-Ausweises wurde <b>2-mal eine falsche Karten-PIN</b> eingegeben. + %1An incorrect PIN has been entered 2 times%2 at the last use of your ID card. + INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. %1 + %2 are used to emphasize. Part 1/2 + Bei der letzten Nutzung ihres Online-Ausweises wurde%1 2 Mal eine falsche Karten-PIN%2 eingegeben. - For a 3rd attempt, the <b>6-digit Card Access Number (CAN)</b> must be entered first. You can find your CAN <b>in the bottom right on the front of your ID card</b>. - INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. Part 2/2 - Für einen 3. Versuch muss die <b>6-stellige Zugangsnummer (CAN)</b> eingeben werden. Sie finden die CAN <b>unten rechts auf der Vorderseite Ihres Ausweises</b>. + For a 3rd attempt, the%1 6-digit Card Access Number (CAN)%2 must be entered first. You can find your CAN %1in the bottom right on the front of your ID card%2. + INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. %1 + %2 are used to emphasize. Part 2/2 + Für einen 3. Versuch muss die%1 6-stellige Zugangsnummer (CAN)%2 eingegeben werden. Sie finden die CAN %1unten rechts auf der Vorderseite Ihres Ausweises%2. - <b>An incorrect PIN has been entered 3 times</b> at the last use of your ID card. - INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. Part 1/2 - Bei der letzten Nutzung ihres Online-Ausweises wurde <b>3-mal eine falsche Karten-PIN</b> eingegeben. + %1An incorrect PIN has been entered 3 times%2 at the last use of your ID card. + INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. %1 + %2 are used to emphasize. Part 1/2 + Bei der letzten Nutzung ihres Online-Ausweises wurde%1 3 Mal eine falsche Karten-PIN%2 eingegeben. - Therefor you have to enter the <b>PUK</b> first to <b>unlock the ID card PIN</b>. - INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. Part 2/2 - Deshalb müssen Sie zunächst die <b>PUK</b> eingeben um die <b>Karten-PIN zu entsperren</b>. + Therefore you have to enter the %1PUK%2 first to %1unlock the ID card PIN%2. + INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. %1 + %2 are used to emphasize. Part 2/2 + Deshalb müssen Sie zunächst die %1PUK%2 eingeben um die %1Karten-PIN zu entsperren%2. @@ -7250,7 +7548,7 @@ If you know neither your Transport PIN nor your ID card PIN, you can request a new PIN using the PIN Reset Service. LABEL ALL_PLATFORMS Hint text for requested Transport PIN but both, Transport PIN and PIN, are not known. - Wenn Sie weder Ihre Transport-PIN, noch Ihre Karten-PIN kennen, können Sie bei Ihrer zuständigen Ausweisbehörde eine neue Karten-PIN setzen. + Wenn Sie weder Ihre Transport-PIN, noch Ihre Karten-PIN kennen, können Sie mit dem PIN-Rücksetzdienst eine neue PIN anfordern. If you don't know your ID card PIN, you may turn to the competent authority and set a new ID card PIN there. @@ -7379,9 +7677,9 @@ Nicht verfügbar - Click to pair - LABEL ALL_PLATFORMS - Klicken zum Koppeln + Tap to pair + LABEL LABEL ANDROID IOS + Tippen zum Koppeln was @@ -7504,7 +7802,7 @@ Nebenbestimmungen I (nur eAT) - Date of expiry + Valid until LABEL ALL_PLATFORMS Gültig bis @@ -7594,6 +7892,10 @@ INFO ALL_PLATFORMS The ID card PIN was changed successfully. Sie haben Ihre Karten-PIN erfolgreich geändert. + + You may now remove your ID card from the device. + Sie können nun Ihren Ausweis vom Gerät entfernen. + governikus::StateCheckRefreshAddress @@ -7923,5 +8225,10 @@ LABEL ALL_PLATFORMS Hint title to assist the user on how to set a new PIN Neue PIN setzen + + Error code: %1 + LABEL ALL_PLATFORMS + Fehlercode: %1 + diff -Nru ausweisapp2-2.3.1/resources/translations/ausweisapp_ru.ts ausweisapp2-2.4.0/resources/translations/ausweisapp_ru.ts --- ausweisapp2-2.3.1/resources/translations/ausweisapp_ru.ts 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/translations/ausweisapp_ru.ts 2025-10-30 10:10:48.000000000 +0000 @@ -42,9 +42,67 @@ ДоÑтупно обновление (верÑÐ¸Ñ %1). - To close the app, press the back button 2 times. + To close the app, tap the back button 2 times. 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). - Чтобы закрыть приложение, два раза нажмите кнопку «Ðазад». + Чтобы закрыть приложение, два раза нажмите кнопку «Ðазад». + + + + AuthCanceledView + + This may take a few moments. + LABEL ALL_PLATFORMS + + + + The authentication was cancelled successfully. + LABEL ALL_PLATFORMS + + + + Back to provider + LABEL ALL_PLATFORMS + + + + Authentication was canceled + LABEL ALL_PLATFORMS + + + + The authentication is being canceled at the provider (%1) + LABEL ALL_PLATFORMS + + + + Back to start page + LABEL ALL_PLATFORMS + Ðазад к начальной Ñтранице + + + The cancellation was triggered on the card reader. + LABEL ALL_PLATFORMS + + + + Back to setup + LABEL ALL_PLATFORM + Ðазад к наÑтройкам + + + You may start a new authentication after clicking the button. + LABEL DESKTOP + + + + You may start a new authentication after tapping the button. + LABEL ANDROID IOS + + + + Cancellation progress + LABEL ALL_PLATFORMS Name of an progress indicator during the cancellation of an authentication read by screen readers + @@ -146,9 +204,14 @@ - Ðе перемещайте карту, пока ÑиÑтема получает к ней доÑтуп - Network problems detected, trying to reach server within 30 seconds. - INFO DESKTOP Information message about cancellation process without working network connectivity - Обнаружены проблемы в Ñети, в течение 30 Ñекунд выполнÑетÑÑ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ðµ к Ñерверу. + Back to start page + LABEL ANDROID IOS + Ðазад к начальной Ñтранице + + + Back to setup + LABEL ANDROID IOS + Ðазад к наÑтройкам Aborting process and informing the service provider @@ -156,19 +219,16 @@ Отмена процеÑÑа и информирование провайдера Ñлужбы - Error code: %1 - INFO DESKTOP Error code (string) of current GlobalStatus code, shown as header of popup. - Код ошибки: %1 - - - Back to start page - LABEL ANDROID IOS - Ðазад к начальной Ñтранице + Network problems detected, trying to reach server within 30 seconds. + INFO DESKTOP Information message about cancellation process without working network connectivity + Обнаружены проблемы в Ñети, в течение 30 Ñекунд выполнÑетÑÑ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ðµ к Ñерверу. - Back to setup - LABEL ANDROID IOS - Ðазад к наÑтройкам + Authentication progress + LABEL DESKTOP Name of an progress indicator during an authentication read by screen readers +---------- +LABEL ANDROID IOS Name of an progress indicator during an authentication read by screen readers + @@ -182,6 +242,39 @@ + AutoRedirectDecision + + Currently, the app automatically redirects you back to the service provider after authentication. This may not allow your screen reader to provide all information. To ensure you receive all information, enable manual redirection to the service provider. + INFO ALL_PLATFORMS + + + + You can change your preference at any time in the settings. + INFO ALL_PLATFORMS + + + + Skip and keep automatic redirection + LABEL ALL_PLATFORMS + + + + Enable manual redirection + INFO ALL_PLATFORMS + + + + Optimize your settings for screen reading + INFO ALL_PLATFORMS + + + + Do not display this message in future. + LABEL ALL_PLATFORMS + Ðе показывать Ñто Ñообщение в будущем. + + + AutostartView Do you want to automatically start the %1 after boot? @@ -223,16 +316,11 @@ - BaseController + BaseHeading - Process finished successfully. You may now remove your ID card from the device. - INFO ALL_PLATFORMS The workflow finished successfully, the ID card may (and should) be removed from the card reader. - ПроцеÑÑ ÑƒÑпешно завершен. Можно извлечь идентификационную карту из уÑтройÑтва. - - - You may now remove your ID card from the device. - INFO ALL_PLATFORMS The workflow is completed, the ID card may (and should) be removed from the card reader. - Можно извлечь идентификационную карту из уÑтройÑтва. + Heading + LABEL WINDOWS Screenreader announcement, that the current item is a heading. + @@ -333,6 +421,94 @@ + CardNotActivatedBaseView + + Activate ID card with PIN reset letter + LABEL ALL_PLATFORMS + + + + Request eID function activation + LABEL ALL_PLATFORMS + + + + eID function is not activated + LABEL ALL_PLATFORMS + + + + Did you recently order a PIN reset letter with an activation code? + LABEL ALL_PLATFORMS + + + + Activate your ID card using the activation code + LABEL ALL_PLATFORMS + + + + Yes, I already have an activation code + LABEL ALL_PLATFORMS + + + + Request the activation of the eID function + LABEL ALL_PLATFORMS + + + + No, I don't have an activation code + LABEL ALL_PLATFORMS + + + + Abort setup + LABEL ALL_PLATFORMS + + + + Enter your activation code of your present PIN reset letter into the following website. + LABEL ALL_PLATFORMS + + + + Enter activation code + LABEL ALL_PLATFORMS + + + + Request action code + LABEL ALL_PLATFORMS + + + + You can request a PIN reset letter with an activation code on the following website. + LABEL ALL_PLATFORMS + + + + Online via PIN reset service + LABEL ALL_PLATFORMS + + + + Find competent authority + LABEL ALL_PLATFORMS + Ðайти компетентный орган + + + You can activate the eID function directly at your competent authority. + LABEL ALL_PLATFORMS + + + + At your competent authority + LABEL ALL_PLATFORMS + + + + CardPositionView Retry @@ -708,9 +884,14 @@ Что Ñто значит? - You may now try the function: "See my personal data". Press the "%1" button to do so now. - LABEL ALL_PLATFORMS - Проверьте функцию: «ПроÑмотреть перÑональные данные». Ðажмите Ð´Ð»Ñ Ñтого кнопку «%1». + You may now try the function: "See my personal data". Click the "%1" button to do so now. + LABEL DESKTOP + Проверьте функцию: «ПроÑмотреть перÑональные данные». Ðажмите Ð´Ð»Ñ Ñтого кнопку «%1». + + + You may now try the function: "See my personal data". Tap the "%1" button to do so now. + LABEL ANDROID IOS + Проверьте функцию: «ПроÑмотреть перÑональные данные». Ðажмите Ð´Ð»Ñ Ñтого кнопку «%1». NFC supported @@ -836,11 +1017,6 @@ Ðе удалоÑÑŒ уÑтановить Ñтабильное Ñоединение Ñ Ð²Ð°ÑˆÐµÐ¹ идентификационной картой.<br><br>ПерезапуÑтите проверку. Попробуйте изменить положение карты и убедитеÑÑŒ в том, что карта не ÑмещаетÑÑ Ð²Ð¾ Ð²Ñ€ÐµÐ¼Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸.<br><br>ЕÑли невозможно уÑтановить Ñоединение Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ картой в разных положениÑÑ…, то Ñто значит, что Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ NFC вашего мобильного уÑтройÑтва не подает доÑтаточно тока на идентификационную карту.<br><br>СпиÑок ÑовмеÑтимых Ñ %1 Ñмартфонов Ñм. на нашем <a href="%2">Ñайте</a>. - Abort setup - LABEL ALL_PLATFORMS - Отменить наÑтройки - - ID card PIN suspended LABEL ALL_PLATFORMS ДейÑтвие PIN-кода идентификационной карты приоÑтановлено @@ -888,21 +1064,11 @@ Поддерживаемые идентификационные карты не обнаружены. %1 поддерживает:<p><ul><li>идентификационные карты Германии;</li><li>Ñлектронные Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð½Ð° временное пребывание (eAT);</li><li>карты онлайн-идентификации граждан ЕС/ЕЭП.</li></ul></p>ЕÑли вы иÑпользуете один из перечиÑленных документов, но данное Ñообщение об ошибке вÑе равно поÑвлÑетÑÑ, перезапуÑтите проверку. - eID function disabled - LABEL ALL_PLATFORMS - Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¾Ð½Ð»Ð°Ð¹Ð½-идентификации деактивирована - - - Activate the eID function. - LABEL ALL_PLATFORMS Hint when a workflow failed because the eID function was not activated - Ðктивируйте функцию онлайн-идентификации. - - - You may continue the onboarding and change your PIN. + You may continue the setup and change your PIN. LABEL ALL_PLATFORMS Sentence 2 of 3 of CAN explanation ---------- LABEL ALL_PLATFORMS Sentence 2 of 3 of PUK explanation - Ð’Ñ‹ можете продолжить наÑтройку и изменить Ñвой PIN-код. + Ð’Ñ‹ можете продолжить наÑтройку и изменить Ñвой PIN-код. @@ -1080,24 +1246,19 @@ Отмена операции - The user interface of the %1 is closed. + How should the %1 be closed in the future? INFO DESKTOP Header of the popup that is shown when the AA is closed for the first time. - ПользовательÑкий Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ %1 закрыт. - - - The %1 is closed. - INFO DESKTOP Header of the popup that is shown when the AA is quit for the first time. - %1 закрыто. + ПользовательÑкий Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ %1 закрыт. - Completely close the app + Close completely LABEL DESKTOP - Завершить приложение полноÑтью + Завершить приложение полноÑтью - Just close the user interface + Leave active in the background LABEL DESKTOP - Завершить только пользовательÑкий Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ + Завершить только пользовательÑкий Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ If the %1 is closed, it is no longer available for authentication. You must then restart the app to authenticate yourself to service providers. @@ -1105,44 +1266,24 @@ ЕÑли приложение %1 завершено, оно больше не может быть иÑпользовано Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸. Вам необходимо перезапуÑтить приложение, чтобы выполнить Ñамоаутентифицикацию Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð²Ð°Ð¹Ð´ÐµÑ€Ð¾Ð² Ñлужбы. - If the %1 is terminated, it is no longer available for authentication. You must then restart the app in order to identify yourself to service providers. - INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is disabled. - ЕÑли %1 завершено, оно больше не доÑтупно Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸. Вам необходимо перезапуÑтить приложение, чтобы выполнить Ñамоидентификацию Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð²Ð°Ð¹Ð´ÐµÑ€Ð¾Ð² Ñлужбы. - - - The app remains available via the icon in the system tray. Click on the %1 icon to reopen the user interface. - INFO DESKTOP Content of the popup that is shown when the AA is closed and the tray icon is enabled. - Приложение оÑтаетÑÑ Ð´Ð¾Ñтупным через значок на панели задач. Ðажмите на значок %1, чтобы Ñнова открыть пользовательÑкий интерфейÑ. - - - The app remains active in the background and can be reopened via the %1 icon on the menu bar again. - INFO DESKTOP Content of the MacOS-popup that is shown when the AA is closed and the tray icon is enabled. - Приложение оÑтаетÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ‹Ð¼ в фоновом режиме и может быть Ñнова открыто Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ значка %1 в Ñтроке меню. - - - The app remains active in the background and can be reopened via the %1 icon in the notification area of the Windows taskbar. - INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is enabled. - Приложение оÑтаетÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ‹Ð¼ в фоновом режиме и может быть Ñнова открыто Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ значка %1 в облаÑти уведомлений на панели задач Windows. - - Do not display this message in future. LABEL DESKTOP Ðе показывать Ñто Ñообщение в будущем. - If you only close the user interface, the app remains active in the background in the future and can be opened again via the %1 icon on the menu bar. - INFO DESKTOP Content of the MacOS-popup that is shown when the AA is closed and the tray icon is disabled. - ЕÑли вы закроете только пользовательÑкий интерфейÑ, приложение будет оÑтаватьÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ‹Ð¼ в фоновом режиме, и его можно будет Ñнова открыть Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ значка %1 в Ñтроке меню. + If the %1 remains active in the background, it will open automatically as soon as you start an authentication. You can still open the %1 manually at any time. + INFO DESKTOP %1 is replaced with the application name + ЕÑли вы закроете только пользовательÑкий интерфейÑ, приложение будет оÑтаватьÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ‹Ð¼ в фоновом режиме, и его можно будет Ñнова открыть Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ значка %1 в Ñтроке меню. - If you only close the user interface, the app remains active in the background in the future and can be reopened via the %1 icon in the notification area of the Windows taskbar. - INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is disabled. - ЕÑли вы закроете только пользовательÑкий интерфейÑ, приложение будет оÑтаватьÑÑ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ‹Ð¼ в фоновом режиме, и его можно будет Ñнова открыть Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ значка %1 в облаÑти уведомлений на панели задач Windows. + Abort process + INFO DESKTOP + - Close user interface to menu bar - LABEL DESKTOP - Закрыть пользовательÑкий Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð² Ñтроке меню + You can change your selection at any time in the settings. + INFO DESKTOP Note to the user that the setting is available in the settings + @@ -1167,7 +1308,7 @@ - DarkModeButtons + DarkModeButtonData System LABEL ALL_PLATFORMS @@ -1257,11 +1398,6 @@ DetachedLogView - Select log: - LABEL DESKTOP - Выбрать файл журнала: - - Zoom: LABEL DESKTOP Изменение маÑштаба изображениÑ: @@ -1306,6 +1442,11 @@ LABEL DESKTOP Фильтр. Деактивирован. + + Current Log + LABEL DESKTOP + + DetachedLogViewWindow @@ -1389,6 +1530,16 @@ LABEL DESKTOP Режим разработчика деактивирует некоторые проверки безопаÑноÑти, и процеÑÑ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸ продолжаетÑÑ Ð´Ð°Ð¶Ðµ при возникновении ошибок. Пропущенные ошибки будут отображены как уведомлениÑ. Режим разработчика можно иÑпользовать только Ñ Ñ‚ÐµÑтовой ИОК. + + Using the developer mode forces the notifications to be enabled. + LABEL DESKTOP Only visible when the user activates the developer mode in the settings. + Ð’ режиме разработчика Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚ÑÑ Ð¿Ñ€Ð¸Ð½ÑƒÐ´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾. + + + Show notifications inside of %1 + LABEL DESKTOP + Показывать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ %1 + DevicesListDelegate @@ -1715,18 +1866,8 @@ GCollapsible - collapse - LABEL ANDROID IOS - Свернуть - - - expand - LABEL ANDROID IOS - Развернуть - - Currently selected is %1 - LABEL ANDROID IOS + LABEL ALL_PLATFORMS Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð¾ %1 @@ -1741,24 +1882,25 @@ GProgressBar - %1 percent done - %1 % выполнено + Progress + LABEL ALL_PLATFORMS + - GStagedProgressBar + GRadioButton - Step %1 of %2. This step is %3 percent complete. - LABEL ALL_PLATFORMS - Шаг %1 из %2 выполнен на %3 процента/процентов. + checked + LABEL DESKTOP + - GText + GStagedProgressBar - Press space to open link - INFO DESKTOP Text read by screen reader if the text contains a weblink which may be opened. - Ðажмите пробел, чтобы открыть ÑÑылку + Step %1 of %2. This step is %3 percent complete. + LABEL ALL_PLATFORMS + Шаг %1 из %2 выполнен на %3 процента/процентов. @@ -1776,11 +1918,6 @@ Поведение - Using the developer mode forces the notifications to be enabled. - LABEL DESKTOP Only visible when the user activates the developer mode in the settings. - Ð’ режиме разработчика Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚ÑÑ Ð¿Ñ€Ð¸Ð½ÑƒÐ´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾. - - Network LABEL DESKTOP Сеть @@ -1796,7 +1933,7 @@ Внешний вид - Use the system font + Use system font LABEL DESKTOP ИÑпользовать ÑиÑтемный шрифт @@ -1811,11 +1948,6 @@ Закрыть окно %1 поÑле аутентификации - Show notifications inside of %1 - LABEL DESKTOP - Показывать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ %1 - - Change language LABEL DESKTOP Изменить Ñзык @@ -1870,6 +2002,31 @@ LABEL WINDOWS Text for attaching the AA to the system tray Прикрепить %1 к панели задач (рекомендуетÑÑ) + + Automatically check for software updates at program start (recommended) + LABEL DESKTOP + + + + Show update + LABEL DESKTOP + Показать обновление + + + Start manual search for software update + LABEL DESKTOP + + + + Abort search + LABEL DESKTOP + + + + When you start %1, it automatically checks for updates. Updates are not performed automatically. If this option is disabled, you have to manually check for updates in the settings. + LABEL DESKTOP %1 is replaced with the application name + + GeneralWorkflow @@ -1883,9 +2040,9 @@ ИÑпользуемое уÑтройÑтво Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚ не ÑоответÑтвует техничеÑким требованиÑм (не поддерживаетÑÑ Ñ€Ð°ÑÑˆÐ¸Ñ€ÐµÐ½Ð½Ð°Ñ Ð´Ð»Ð¸Ð½Ð°). - Place ID card + Read out ID card with connected device LABEL DESKTOP - Ð’Ñтавьте идентификационную карту + Connect USB card reader or smartphone @@ -1965,12 +2122,12 @@ Wrong new ID card PIN confirmation LABEL ALL_PLATFORMS - Ðеправильное подтверждение нового PIN-кода идентификационной карты + Ðеправильное подтверждение нового PIN-кода идентификационной карты Wrong new Smart-eID PIN confirmation LABEL ALL_PLATFORMS - Ðеправильное подтверждение нового PIN-кода Ð´Ð»Ñ Smart-eID + Ðеправильное подтверждение нового PIN-кода Ð´Ð»Ñ Smart-eID @@ -2086,6 +2243,19 @@ + LinkQualityAnimation + + Link quality unavailable. + INFO ALL_PLATFORMS + + + + %1% link quality. + INFO ALL_PLATFORMS %1 is replaced with a number between 0 and 100 + + + + LocalNetworkInfo Go to application settings @@ -2099,15 +2269,45 @@ + LogFilesView + + Select Log + LABEL ANDROID IOS + + + + Delete + LABEL ANDROID IOS + Удалить + + + All old logs will be deleted. + INFO ANDROID IOS All logfiles are about to be removed, user confirmation required. + Ð’Ñе Ñтарые файлы журнала будут удалены. + + + Delete all logs + LABEL ANDROID IOS + Удалить вÑе файлы журнала + + + LogTitleBarControls Share log + LABEL ANDROID IOS ПоделитьÑÑ Ñ„Ð°Ð¹Ð»Ð¾Ð¼ журнала Delete all logs + LABEL ANDROID IOS Удалить вÑе файлы журнала + + Filter + LABEL ANDROID IOS + Фильтр + LogView @@ -2136,40 +2336,20 @@ All old logs will be deleted. - INFO DESKTOP All logfiles are about to be removed, user confirmation required. ----------- -INFO ANDROID IOS All logfiles are about to be removed, user confirmation required. + INFO DESKTOP All logfiles are about to be removed, user confirmation required. Ð’Ñе Ñтарые файлы журнала будут удалены. - Log - LABEL ANDROID IOS - Файл журнала - - - Select log from list. - Выберите файл журнала из ÑпиÑка. - - Delete all logs - LABEL DESKTOP ----------- -LABEL ANDROID IOS + LABEL DESKTOP Удалить вÑе файлы журнала Delete - LABEL DESKTOP ----------- -LABEL ANDROID IOS + LABEL DESKTOP Удалить - The log entry was copied to the clipboard. - INFO ANDROID IOS Toast message used to confirm the copy of a log entry. - ЗапиÑÑŒ журнала Ñкопирована в буфер обмена. - - Filter LABEL ANDROID IOS Фильтр @@ -2194,7 +2374,9 @@ LogViewDelegate The log entry was copied to the clipboard. - INFO DESKTOP Toast message used to confirm the copy of a log entry. + INFO DESKTOP Toast message used to confirm the copy of a log entry. +---------- +INFO ANDROID IOS Toast message used to confirm the copy of a log entry. ЗапиÑÑŒ журнала Ñкопирована в буфер обмена. @@ -2275,9 +2457,9 @@ ÐžÐ±Ñ‰Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ - Version information - LABEL ANDROID IOS - Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ верÑии + %1 version + LABEL ANDROID IOS %1 is replaced with the application name + Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ верÑии Software license @@ -2354,9 +2536,9 @@ УÑÐ»Ð¾Ð²Ð¸Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ Ð»Ð¸Ñ†ÐµÐ½Ð·Ð¸Ñ Ð½Ð° программное обеÑпечение - Start onboarding + Start setup LABEL ANDROID IOS - Ðачать наÑтройку + Ðачать наÑтройку @@ -2405,14 +2587,14 @@ Контакты - Start onboarding + Start setup LABEL DESKTOP - Ðачать наÑтройку + Ðачать наÑтройку - Onboarding + Setup LABEL DESKTOP - ÐаÑтройка + ÐаÑтройка @@ -2483,8 +2665,10 @@ 5-значный временный PIN-код - Ñто %1одноразовый PIN-код%2, указанный %1в пиÑьме%2, полученным вами при подачи заÑвки на получение идентифиакационой карты. - When setting up your ID card, you will %1replace%2 this 5-digit %1Transport PIN with%2 a 6-digit, %1self chosen card PIN%2. - INFO ALL_PLATFORMS Answer to the question 'Where do I find the Transport PIN?' paragraph 2/2 + When you set up your ID card, you will %1replace%2 this 5-digit %1Transport PIN with%2 a 6-digit, %1self chosen card PIN%2. + INFO ALL_PLATFORMS Answer to the question 'Where do I find the Transport PIN?' paragraph 2/2 +---------- +INFO ALL_PLATFORMS Description text explaining the PINs 3/7 При наÑтройке идентификационной карты вы %1замените%2 Ñтот 5-значный %1временный PIN-код на%2 6-значный, %1 ÑамоÑтоÑтельно выбранный вами PIN-код карты%2. @@ -2493,9 +2677,9 @@ Где найти PUK-код? - The PUK is a %1 10-digit number%2 that you can find in the %1PIN letter%2 that was sent to you by mail after you %1applied for your ID card%2. You may find it to the %1right%2 of the 5-digit %1Transport PIN%2. + The PUK is a%1 10-digit number%2 that you can find in the %1PIN letter%2 that was sent to you by mail after you %1applied for your ID card%2. You may find it to the %1right%2 of the 5-digit %1Transport PIN%2. LABEL ALL_PLATFORMS INFO ALL_PLATFORMS Answer to the question 'Where do I find the PUK?' - PUK-код - Ñто %1 10-значное чиÑло%2, которое вы найдете в %1пиÑьме Ñ PIN-кодом%2, отправленное вам по почте поÑле подачи вами %1заÑвки на получение идентификационной карты%2. Он указан %1Ñправа%2 от 5-значного %1временного PIN-кода%2. + PUK-код - Ñто%1 10-значное чиÑло%2, которое вы найдете в %1пиÑьме Ñ PIN-кодом%2, отправленное вам по почте поÑле подачи вами %1заÑвки на получение идентификационной карты%2. Он указан %1Ñправа%2 от 5-значного %1временного PIN-кода%2. Why is the PUK required? @@ -2503,7 +2687,7 @@ Ð”Ð»Ñ Ñ‡ÐµÐ³Ð¾ необходим PUK-код? - The PUK is required if the %1card PIN has been entered incorrectly 3 times%2 in a row. As a result, the card PIN is blocked. By entering the PUK you will %1unblock the card PIN%2 and have %1 3 more attempts%2 to enter the correct PIN. + The PUK is required if the %1card PIN has been entered incorrectly 3 times%2 in a row. As a result, the card PIN is blocked. By entering the PUK you will %1unblock the card PIN%2 and have%1 3 more attempts%2 to enter the correct PIN. INFO ALL_PLATFORMS Answer to the question 'Why is the PUK required?' PUK-код понадобитÑÑ, еÑли %1PIN-код карты будет введен неправильно 3 раза%2 подрÑд. Это приводит к блокировке PIN-кода карты. Ð’Ð²ÐµÐ´Ñ PUK-код, вы %1разблокируете PIN-код карты %2 и получите %1еще 3 попытки%2 Ð´Ð»Ñ Ð²Ð²Ð¾Ð´Ð° правильного PIN-кода. @@ -2645,11 +2829,6 @@ Изменить 6-значный PIN-код Ð´Ð»Ñ Smart-eID можно в любое Ð²Ñ€ÐµÐ¼Ñ Ð¸ неограниченное количеÑтво раз, еÑли вы знаете Ñвой дейÑтвительный PIN-код Ð´Ð»Ñ Smart-eID. - When you set up the eID function, you will %1replace%2 this 5-digit %1Transport PIN%2 with a 6-digit %1card PIN that you choose yourself%2. - INFO ALL_PLATFORMS Description text explaining the PINs 3/7 - При наÑтройке функции онлайн-идентификации вы %1замените%2 Ñтот 5-значный %1временный PIN-код %2 на 6-значный %1 PIN-код карты, выбранный вами ÑамоÑтоÑтельно%2. - - The Smart-eID PIN also has six digits. You also choose that PIN yourself while setting up the Smart-eID for the first time. INFO ALL_PLATFORMS Description text explaining the PINs 5/7 PIN-код Ð´Ð»Ñ Smart-eID ÑоÑтоит из шеÑти знаков. Ð’Ñ‹ выбираете Ñтот PIN-код Ñами при первой наÑтройке Smart-eID. @@ -2700,9 +2879,9 @@ У Ð¼ÐµÐ½Ñ ÐµÑть только 5-значный временный PIN-код - You need to change the %1 5-digit Transport PIN%2 to your personal card PIN. Use %1Change PIN > Transport PIN%2 from the startpage to do so. + You need to change the%1 5-digit Transport PIN%2 to your personal card PIN. Use %1Change PIN > Transport PIN%2 from the startpage to do so. INFO ALL_PLATFORMS Explanation if only the Transport PIN is at hand - Вам необходимо заменить %1 5-значный временный PIN-код%2 на PIN-код вашей карты. Ð”Ð»Ñ Ñтого воÑпользуйтеÑÑŒ пунктом меню%1изменить PIN-код > временный PIN-код%2 на Ñтартовой Ñтранице. + Вам необходимо заменить%1 5-значный временный PIN-код%2 на PIN-код вашей карты. Ð”Ð»Ñ Ñтого воÑпользуйтеÑÑŒ пунктом меню%1изменить PIN-код > временный PIN-код%2 на Ñтартовой Ñтранице. If you have forgotten your Smart-eID PIN, you can renew your Smart-eID and thereby set a new PIN. @@ -2768,9 +2947,9 @@ ПоÑле уÑтановки 6-значного PIN-кода временный PIN-код ÑтановитÑÑ Ð½ÐµÐ´ÐµÐ¹Ñтвительным. Ð’ дальнейшем Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸ может быть иÑпользован только 6-значный PIN-код. - The 6-digit card PIN is a %1number that you choose yourself%2 when you set up the eID function for the first time. It %1replaces%2 your %1 5-digit Transport PIN%2. + The 6-digit card PIN is a %1number that you choose yourself%2 when you set up the eID function for the first time. It %1replaces%2 your%1 5-digit Transport PIN%2. INFO ALL_PLATFORMS Description text explaining the PINs 4/7 - 6-значный PIN-код карты - Ñто ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñ %1цифр, которую вы выбираете ÑамоÑтоÑтельно%2 при первой наÑтройке функции онлайн-идентификации. Он %1заменÑет%2 ваш %1 5-значный временный PIN-код%2. + 6-значный PIN-код карты - Ñто ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñ %1цифр, которую вы выбираете ÑамоÑтоÑтельно%2 при первой наÑтройке функции онлайн-идентификации. Он %1заменÑет%2 ваш%1 5-значный временный PIN-код%2. Set up the ID card in three steps @@ -2917,12 +3096,19 @@ NavigationAction Cancel + LABEL DESKTOP Отмена Back + LABEL DESKTOP Ðазад + + Close + LABEL DESKTOP + Закрыть + NavigationView @@ -3049,7 +3235,7 @@ NFC scan is not running - INFO ANDROID IOS NFC is available and enabled but needs to be started. + INFO ANDROID IOS NFC-Ñканирование не выполнÑетÑÑ @@ -3069,7 +3255,7 @@ Start scan - INFO ANDROID IOS + INFO ANDROID IOS NFC is available and enabled but needs to be started. Ðачать Ñканирование @@ -3154,30 +3340,48 @@ + Notifications + + Notification: %1 + LABEL DESKTOP %1 will be replaced with a notification text + + + + NumberField The number is hidden. - LABEL DESKTOP Screenreader text for the password field + LABEL ALL_PLATFORMS Screenreader text for the password field Ðомер Ñкрыт. You entered %1 of %2 digits. - LABEL DESKTOP Screenreader text for the password field + LABEL ALL_PLATFORMS Screenreader text for the password field Ð’Ñ‹ ввели %1 из %2 знаков. - Press to hide the number + Click to hide the number LABEL DESKTOP Screenreader text for the eye icon to change the password visibility - Ðажмите, чтобы Ñкрыть номер + Ðажмите, чтобы Ñкрыть номер + + + Tap to hide the number + LABEL ANDROID IOS Screenreader text for the eye icon to change the password visibility + Ðажмите, чтобы Ñкрыть номер - Press to show the number + Click to show the number LABEL DESKTOP Screenreader text for the eye icon to change the password visibility - Ðажмите, чтобы показать номер + Ðажмите, чтобы показать номер + + + Tap to show the number + LABEL ANDROID IOS Screenreader text for the eye icon to change the password visibility + Ðажмите, чтобы показать номер The number is visible. Digits entered so far: %1 - LABEL DESKTOP Screenreader text for the password field + LABEL ALL_PLATFORMS Screenreader text for the password field Ðомер отображаетÑÑ. Цифры, введенные на данный момент: %1 @@ -3425,9 +3629,9 @@ К Ñожалению, у Ð²Ð°Ñ Ð½Ðµ выполнены вÑе обÑзательные Ñ‚Ñ€ÐµÐ±Ð¾Ð²Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ %1. - You may restart the setup anytime under %1Help > Onboarding%2. + You may restart the setup anytime under %1Help > Setup%2. LABEL ALL_PLATFORMS %1 and %2 are replaced with bold emphasis. - Ð’Ñ‹ можете в любое Ð²Ñ€ÐµÐ¼Ñ Ð²ÐµÑ€Ð½ÑƒÑ‚ÑŒÑÑ Ðº наÑтройкам через %1 меню: Справка > ÐаÑтройка%2. + Ð’Ñ‹ можете в любое Ð²Ñ€ÐµÐ¼Ñ Ð²ÐµÑ€Ð½ÑƒÑ‚ÑŒÑÑ Ðº наÑтройкам через %1 меню: Справка > ÐаÑтройка%2. The setup will now be aborted - afterwards %1one prompt regarding personalized settings in the %3%2 will follow. @@ -3545,14 +3749,9 @@ или - Afterwards, a pairing code is displayed on your smartphone. You may then select the device in the list below to enter the pairing code. - LABEL DESKTOP - Затем на Ñкране Ñмартфона поÑвитÑÑ ÐºÐ¾Ð´ Ð´Ð»Ñ ÑопрÑжениÑ. ПоÑле Ñтого в ÑпиÑке ниже вы Ñможете выбрать уÑтройÑтво Ð´Ð»Ñ Ð²Ð²Ð¾Ð´Ð° кода ÑопрÑжениÑ. - - - Press space to continue onboarding using the smartphone "%1" - LABEL DESKTOP - Ðажмите пробел, чтобы продолжить наÑтройку Ñмартфона «%1». + Afterwards, a pairing code is displayed on your smartphone. You may then select the device in the %1 list to enter the pairing code. + LABEL DESKTOP %1 will be replaced with the Available Devices list name + Затем на Ñкране Ñмартфона поÑвитÑÑ ÐºÐ¾Ð´ Ð´Ð»Ñ ÑопрÑжениÑ. ПоÑле Ñтого в ÑпиÑке ниже вы Ñможете выбрать уÑтройÑтво Ð´Ð»Ñ Ð²Ð²Ð¾Ð´Ð° кода ÑопрÑжениÑ. Use device @@ -3608,12 +3807,12 @@ I use the eID function for the 1st time LABEL ALL_PLATFORMS - Я иÑпользую функцию онлайн-идентификации впервые. + Я иÑпользую функцию онлайн-идентификации впервые I already used the eID function before LABEL ALL_PLATFORMS - Я пользовальÑÑ Ñ€Ð°Ð½ÑŒÑˆÐµ функцией онлайн-идентификации. + Я пользовальÑÑ Ñ€Ð°Ð½ÑŒÑˆÐµ функцией онлайн-идентификации Skip setup @@ -3630,7 +3829,9 @@ OnboardingView Continue - LABEL DESKTOP + LABEL DESKTOP +---------- +LABEL ANDROID IOS Продолжить @@ -3650,24 +3851,34 @@ Open %1 on your %2other device%3. - LABEL ANDROID IOS Assistance text for pairing new devices. Step 1 of 4 + LABEL ANDROID IOS Assistance text for pairing new devices. Step 1 of 5 Откройте %1 в другом %2вашем уÑтройÑтве%3. - On that device go to %1Settings%2 and then %1Smartphone as card reader%2 resp. %1Manage pairings%2. - LABEL ANDROID IOS Assistance text for pairing new devices. Step 2 of 4. %1 and %2 are surrounding tags for bold font. - Ð’ данном уÑтройÑтве перейдите в меню %1ÐаÑтройки%2, а затем %1Смартфон в качеÑтве уÑтройÑтва Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚%2 и в ÑоответÑтвующее меню %1Управление ÑопрÑжениÑми%2. + Make sure that both devices are on the %1same network%2 (e.g. WiFi). + LABEL ANDROID IOS Assistance text for pairing new devices. Step 2 of 5. %1 and %2 are surrounding tags for bold font. + + + + On your other device, go to %1Settings%2 and then %1Smartphone as card reader%2 resp. %1Manage pairings%2. + LABEL ANDROID IOS Assistance text for pairing new devices. Step 3 of 5. %1 and %2 are surrounding tags for bold font. + Choose this smartphone in the list to pair it. - LABEL ANDROID IOS Assistance text for pairing new devices. Step 3 of 4 + LABEL ANDROID IOS Assistance text for pairing new devices. Step 4 of 5 Выберите данный Ñмартфон в ÑпиÑке, чтобы выполнить ÑопрÑжение. Enter the pairing code "%1". - LABEL ANDROID IOS Provide pairing code. Step 4 of 4 + LABEL ANDROID IOS Provide pairing code. Step 5 of 5 Введите код ÑопрÑÐ¶ÐµÐ½Ð¸Ñ Â«%1». + + Please follow these steps: + LABEL ANDROID IOS + + PairingFailedView @@ -3692,7 +3903,7 @@ Then select %1Pair (new) device%2. You may need to activate NFC first. LABEL ALL_PLATFORM Assistance text for pairing new devices. Step 4 of 5. %1 and %2 are for bold formatting. - Затем выберите %1СопрÑжение (нового) уÑтройÑтва%2. Возможно, вам Ñначала нужно будет включить NFC + Затем выберите %1СопрÑжение (нового) уÑтройÑтва%2. Возможно, вам Ñначала нужно будет включить NFC. As soon as a pairing code is displayed on the smartphone, you can use it for pairing here. @@ -3956,18 +4167,23 @@ PkiSwitch - %1 more presses to toggle the environment (prod/test) for integrated functions. + %1 more clicks to toggle the environment (prod/test) for integrated functions. + INFO DESKTOP Used in notifications when the user taps the icon + %1 Ðажатие (-Ñ/-й) Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñреды (произв./теÑÑ‚.) Ð´Ð»Ñ Ð²Ñтроенных функций. + + + %1 more taps to toggle the environment (prod/test) for integrated functions. INFO ANDROID IOS Used in notifications when the user taps the icon - %1 Ðажатие (-Ñ/-й) Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñреды (произв./теÑÑ‚.) Ð´Ð»Ñ Ð²Ñтроенных функций. + %1 Ðажатие (-Ñ/-й) Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ñреды (произв./теÑÑ‚.) Ð´Ð»Ñ Ð²Ñтроенных функций. Testmode for the integrated functions activated. - INFO ANDROID IOS Used in notifications when the user taps the icon + INFO ALL_PLATFORMS Used in notifications when the user taps the icon ТеÑтовый режим Ð´Ð»Ñ Ð²Ñтроенных функций активирован. Testmode for the integrated functions deactivated. - INFO ANDROID IOS Used in notifications when the user taps the icon + INFO ALL_PLATFORMS Used in notifications when the user taps the icon ТеÑтовый режим Ð´Ð»Ñ Ð²Ñтроенных функций деактивирован. @@ -4037,11 +4253,14 @@ Ð’Ñ‹ можете иÑпользовать Ñвой Ñмартфон %2 Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸ÐµÐ¹ NFC%3. Ð”Ð»Ñ Ñтого вам необходимо уÑтановить %1 на Ñтом Ñмартфоне. Ð’Ñ‹ также можете иÑпользовать %USB-уÑтройÑтво Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚ Ñ %2компьютером%3. - You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a %1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, click here: - LABEL DESKTOP %1 + %2 = Bold Tags, %3 = AusweisApp ----------- -LABEL ANDROID IOS %1 + %2 = Bold Tags, %3 = AusweisApp - Ð’Ñ‹ получили одноразовый PIN-код %1временный PIN-код%2 в пиÑьме от компетентнго органа. Ð’Ñ‹ можете заменить его на %1 6-значный PIN-код карты%2 в %3 или в бюро обÑÐ»ÑƒÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð½Ð°ÑелениÑ. ЕÑли у Ð²Ð°Ñ Ð½ÐµÑ‚ PIN-кода или вы не помните PIN-код Ñвоей карты, нажмите Ñюда: + You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a%1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, click here: + LABEL DESKTOP %1 + %2 = Bold Tags, %3 = AusweisApp + Ð’Ñ‹ получили одноразовый PIN-код %1временный PIN-код%2 в пиÑьме от компетентнго органа. Ð’Ñ‹ можете заменить его на%1 6-значный PIN-код карты%2 в %3 или в бюро обÑÐ»ÑƒÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð½Ð°ÑелениÑ. ЕÑли у Ð²Ð°Ñ Ð½ÐµÑ‚ PIN-кода или вы не помните PIN-код Ñвоей карты, нажмите Ñюда: + + + You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a%1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, tap here: + LABEL ANDROID IOS %1 + %2 = Bold Tags, %3 = AusweisApp + Ð’Ñ‹ получили одноразовый PIN-код %1временный PIN-код%2 в пиÑьме от компетентнго органа. Ð’Ñ‹ можете заменить его на%1 6-значный PIN-код карты%2 в %3 или в бюро обÑÐ»ÑƒÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ Ð½Ð°ÑелениÑ. ЕÑли у Ð²Ð°Ñ Ð½ÐµÑ‚ PIN-кода или вы не помните PIN-код Ñвоей карты, нажмите Ñюда: The chip in your ID card is read using %1NFC%2. To do this, simply place the ID card on the %1back of the smartphone%2. @@ -4097,9 +4316,9 @@ Ваши перÑональные данные не ÑохранÑÑŽÑ‚ÑÑ Ð¸ не обрабатываютÑÑ. ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾Ð± обработке перÑональных данных: Ñм. %1. - data privacy statement of the Federal Ministry of the Interior and Community + data privacy statement of the Federal Ministry of the Interior LABEL ALL_PLATFORMS Text of the Smart-eID html link inside of a sentence - ЗаÑвление о конфиденциальноÑти данных Федерального миниÑтерÑтва внутренних дел + ЗаÑвление о конфиденциальноÑти данных Федерального миниÑтерÑтва внутренних дел data privacy statement @@ -4130,9 +4349,9 @@ Провайдер - Touch for more details + Tap for more details LABEL ANDROID IOS - Ðажмите Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ð¹ информации + Ðажмите Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ð¹ информации Details about the provider @@ -4216,6 +4435,24 @@ + ReaderFoundConfirmation + + Found new smartphone as card reader that is suitable for the ID card. The workflow may now be continued. + LABEL DESKTOP + + + + Found new USB card reader that is suitable for the ID card. The workflow may now be continued. + LABEL DESKTOP + Ðайдено новое USB-уÑтройÑтво Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚, подходÑщее Ð´Ð»Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ карты. Теперь можно продолжить процеÑÑ. + + + Continue + LABEL DESKTOP + Продолжить + + + RedirectView Remove the ID card from the card reader @@ -4233,14 +4470,24 @@ ЕÑли у Ð²Ð°Ñ Ð²Ð¾Ð·Ð½Ð¸ÐºÐ»Ð¸ вопроÑÑ‹ или вы обнаружили ошибки в процеÑÑе, обратитеÑÑŒ, пожалуйÑта, к ÑоответÑтвующему провайдеру. - You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, click on the "%1" button. - INFO ALL_PLATFORMS Redirect information when automatic redirect is enabled - Через неÑколько Ñекунд Ð²Ð°Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки перенаправÑÑ‚ к провайдеру. ЕÑли перенаправление не произойдет автоматичеÑки, нажмите на кнопку «%1». + You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, click the "%1" button. + INFO DESKTOP Redirect information when automatic redirect is enabled + Через неÑколько Ñекунд Ð²Ð°Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки перенаправÑÑ‚ к провайдеру. ЕÑли перенаправление не произойдет автоматичеÑки, нажмите на кнопку «%1». - Press the button to complete the authentication and return to the provider. - INFO ALL_PLATFORMS Redirect information when automatic redirect is disabled - Ðажмите кнопку, чтобы завершить аутентификацию и вернутьÑÑ Ðº провайдеру. + Click the button to complete the authentication and return to the provider. + INFO DESKTOP Redirect information when automatic redirect is disabled + Ðажмите кнопку, чтобы завершить аутентификацию и вернутьÑÑ Ðº провайдеру. + + + You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, tap the "%1" button. + INFO ANDROID IOS Redirect information when automatic redirect is enabled + Через неÑколько Ñекунд Ð²Ð°Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки перенаправÑÑ‚ к провайдеру. ЕÑли перенаправление не произойдет автоматичеÑки, нажмите на кнопку «%1». + + + Tap the button to complete the authentication and return to the provider. + INFO ANDROID IOS Redirect information when automatic redirect is disabled + Ðажмите кнопку, чтобы завершить аутентификацию и вернутьÑÑ Ðº провайдеру. Return to provider @@ -4271,28 +4518,29 @@ RemoteReaderDelegate - Smartphone named "%1". %2. - INFO DESKTOP Name and status of remote device. %1 is replaced with the name, %2 with the status - Ð˜Ð¼Ñ Ñмартфона «%1». %2. + Smartphone named "%1". + INFO DESKTOP Name of remote device. %1 is replaced with the name. + Ð˜Ð¼Ñ Ñмартфона «%1». - Press space to unpair the smartphone "%1". - INFO DESKTOP Text for activation action if the device is paired. - Ðажмите пробел, чтобы отменить ÑопрÑжение Ñмартфона «%1». + Status: "%1". + INFO DESKTOP Status of remote device. %1 is replaced with the status. + - Press space to pair the smartphone "%1". - INFO DESKTOP Text for activation action if the device is unpaired. - Ðажмите пробел Ð´Ð»Ñ ÑопрÑÐ¶ÐµÐ½Ð¸Ñ Ñмартфона «%1». + Pair + LABEL DESKTOP + СопрÑжение - Remove remote device - Удалить диÑтанционное уÑтройÑтво + Unpair + LABEL DESKTOP + - Pair - LABEL DESKTOP - СопрÑжение + %1 device "%2" + LABEL DESKTOP Text of pairing button, %1 will be Pair/Unpair and %2 is replaced with device name + @@ -4349,9 +4597,9 @@ ПоÑледние подключенные - Click to remove device + Tap to remove device LABEL ANDROID IOS - Ðажмите Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ ÑƒÑтройÑтва + Ðажмите Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ ÑƒÑтройÑтва Remove @@ -4374,9 +4622,9 @@ Добавить ÑопрÑжение - Click to pair + Tap to pair LABEL ANDROID IOS - Ðажмите Ð´Ð»Ñ ÑопрÑÐ¶ÐµÐ½Ð¸Ñ + Ðажмите Ð´Ð»Ñ ÑопрÑÐ¶ÐµÐ½Ð¸Ñ Please connect your WiFi to use another smartphone as card reader (SaC). @@ -4399,9 +4647,9 @@ СопрÑжение уÑтройÑтва… - Click to use device + Tap to use device LABEL ANDROID IOS - Ðажмите, чтобы иÑпользовать уÑтройÑтво + Ðажмите, чтобы иÑпользовать уÑтройÑтво @@ -4429,12 +4677,7 @@ To do this, start a process on a paired device. INFO ANDROID IOS - ЗапуÑтите Ð´Ð»Ñ Ñтого процеÑÑ Ð½Ð° ÑопрÑженном уÑтройÑтве - - - Pairing code: <b>%1</b> - LABEL ANDROID IOS - Код ÑопрÑжениÑ: <b>%1</b> + ЗапуÑтите Ð´Ð»Ñ Ñтого процеÑÑ Ð½Ð° ÑопрÑженном уÑтройÑтве. Enable WiFi @@ -4496,23 +4739,13 @@ Где Ñледует вводить код ÑопрÑжениÑ? - Enter the pairing code "%1" in the %2 on your other device. - INFO ANDROID IOS - Введите код ÑопрÑÐ¶ÐµÐ½Ð¸Ñ "%1" в поле %2 в другом вашем уÑтройÑтве. - - Cancel pairing LABEL ANDROID IOS Отменить ÑопрÑжение - Activate the card reader, this allows the paired devices to use this smartphone as a card reader. - INFO ANDROID IOS - Ðктивируйте уÑтройÑтво Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚. Это позволит ÑопрÑженным уÑтройÑтвам иÑпользовать Ñтот Ñмартфон в качеÑтве уÑтройÑтва Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚. - - Paired devices may use this Smartphone as a card reader now. - INFO ANDROID IOS + LABEL ANDROID IOS Теперь ÑопрÑженные уÑтройÑтва могут иÑпользовать Ñтот Ñмартфон в качеÑтве уÑтройÑтва Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚. @@ -4525,13 +4758,55 @@ LABEL ANDROID IOS ОÑтановить уÑтройÑтво Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚ - - - RemoteServiceWifiInfo + + No device paired + LABEL ANDROID IOS + + + + Card reader not active + LABEL ANDROID IOS + + + + Use this smartphone as a card reader for a paired device + LABEL ANDROID IOS + + + + Activate the card reader + LABEL ANDROID IOS + + Both devices have to be on the same network (e.g. WiFi). INFO ANDROID IOS The remote service is active. Hint that both devices need to be connected to the same network. - Оба уÑтройÑтва должны быть подключены к одной Ñети (например, Wi-Fi). + Оба уÑтройÑтва должны быть подключены к одной Ñети (например, Wi-Fi). + + + Pairing code: + LABEL ANDROID IOS + + + + This allows the paired devices to use this smartphone as a card reader. + INFO ANDROID IOS + + + + Enter the pairing code + LABEL ANDROID IOS + + + + Enter the pairing code "%1" in the %2 on your other device. Both devices have to be on the same network (e.g. WiFi). + INFO ANDROID IOS %1 is replaced with the pairing code, %2 with the name "AusweisApp" + + + + Pairing progress + LABEL ANDROID IOS Name of an progress indicator during the pairing process read by screen readers + @@ -4585,11 +4860,6 @@ ResultErrorView - Error code: - LABEL ANDROID IOS - Код ошибки: - - Show Details LABEL ANDROID IOS Показать подробную информацию @@ -4638,46 +4908,6 @@ Ð¦Ð¸Ñ„Ñ€Ð¾Ð²Ð°Ñ ÐºÐ»Ð°Ð²Ð¸Ð°Ñ‚ÑƒÑ€Ð° - Software updates - LABEL DESKTOP - ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¼Ð½Ð¾Ð³Ð¾ обеÑÐ¿ÐµÑ‡ÐµÐ½Ð¸Ñ - - - Check for updates at program start - LABEL DESKTOP - Проверка обновлений при запуÑке программы - - - Show update - LABEL DESKTOP - Показать обновление - - - Check now - LABEL DESKTOP - Проверить ÑÐµÐ¹Ñ‡Ð°Ñ - - - An update is available (version %1)! - LABEL DESKTOP An update is available, the new version is supplied to the user. - ДоÑтупно обновление (верÑÐ¸Ñ %1)! - - - An update is available but retrieving the information failed. - LABEL DESKTOP The updater found an update but not all required update information are valid, this should be a very rare case. - Обновление доÑтупно, но произошел Ñбой при загрузке информации. - - - Your version %1 of %2 is up to date. - LABEL DESKTOP The current version is up to date, no user action is required. - Ваша верÑÐ¸Ñ %2 %1 ÑвлÑетÑÑ Ð°ÐºÑ‚ÑƒÐ°Ð»ÑŒÐ½Ð¾Ð¹. - - - No update information available, please check for update manually. - LABEL DESKTOP The automatic update check is disabled (or no network connection was present during app start), a manual check for update is required. - Ðет информации об обновлениÑÑ…, проверьте наличие доÑтупных обновлений вручную. - - Shuffle keys LABEL DESKTOP Случайное раÑположение кнопок Ñкранной клавиатуры @@ -4729,16 +4959,6 @@ SelfAuthenticationData - Read self-authentication data - LABEL DESKTOP Title of the self authentication result data view - Считать данные Ñамоаутентификации - - - Successfully read data - INFO DESKTOP Status message that the self authentication successfully completed. - Данные Ñчитаны уÑпешно - - Read data LABEL DESKTOP Title of the self authentication result data view Считать данные @@ -4755,6 +4975,19 @@ + SelfAuthenticationHeader + + Successfully read data. + INFO ALL_PLATFORMS Status message that the self authentication successfully completed (1/2). + Данные Ñчитаны уÑпешно. + + + You may now remove your ID card from the device. + INFO ALL_PLATFORMS Status message that the self authentication successfully completed (2/2). + Можно извлечь идентификационную карту из уÑтройÑтва. + + + SettingsView Settings @@ -5020,9 +5253,9 @@ Следуйте инÑтрукциÑм на втором уÑтройÑтве - Now follow the instruction for the setup on your PC/Mac. If the onboarding does not start automatically, you may find it under Help > Onboarding. + Now follow the instruction for the setup on your PC/Mac. If the setup does not start automatically, you may find it under Help > Setup. LABEL ANDROID IOS - Теперь Ñледуйте инÑтрукциÑм по наÑтройке на вашем компьютере/Mac. ЕÑли процеÑÑ Ð½Ð°Ñтройки не запуÑтитÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки, вы Ñможете найти информацию здеÑÑŒ: Справка > ÐаÑтройка + Теперь Ñледуйте инÑтрукциÑм по наÑтройке на вашем компьютере/Mac. ЕÑли процеÑÑ Ð½Ð°Ñтройки не запуÑтитÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки, вы Ñможете найти информацию здеÑÑŒ: Справка > ÐаÑтройка. Continue with pairing @@ -5301,7 +5534,7 @@ Sample card required. LABEL ANDROID IOS - Ðеобходима Ð¾Ð±Ñ€Ð°Ð·Ñ†Ð¾Ð²Ð°Ñ Ñ‚ÐµÑÑ‚Ð¾Ð²Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð° + Ðеобходима Ð¾Ð±Ñ€Ð°Ð·Ñ†Ð¾Ð²Ð°Ñ Ñ‚ÐµÑÑ‚Ð¾Ð²Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð°. @@ -5329,7 +5562,7 @@ Sample card required. LABEL ANDROID IOS - Ðеобходима Ð¾Ð±Ñ€Ð°Ð·Ñ†Ð¾Ð²Ð°Ñ Ñ‚ÐµÑÑ‚Ð¾Ð²Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð° + Ðеобходима Ð¾Ð±Ñ€Ð°Ð·Ñ†Ð¾Ð²Ð°Ñ Ñ‚ÐµÑÑ‚Ð¾Ð²Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð°. @@ -5373,9 +5606,9 @@ Ð­Ð»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð° Smart-eID не готова - Your Smart-eID is ready for use, press "Continue" to proceed. + Your Smart-eID is ready for use, tap "Continue" to proceed. LABEL ANDROID IOS - Ваша ÑÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð° Smart-eID готова к иÑпользованию, нажмите «Продолжить» Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ. + Ваша ÑÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð° Smart-eID готова к иÑпользованию, нажмите «Продолжить» Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸Ñ. Please wait a moment. @@ -5465,6 +5698,19 @@ + TabbedPane + + Content of tab "%1" + LABEL DESKTOP %1 will be replaced with the title of the tab + + + + Sidebar + LABEL DESKTOP + + + + TabbedPaneDelegate %1 of %2 @@ -5476,11 +5722,16 @@ LABEL DESKTOP Вкладка выбрана + + Tab + LABEL DESKTOP + Вкладка + TabbedReaderView - Card Readers + Card readers LABEL DESKTOP УÑтройÑтва Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚ @@ -5492,10 +5743,6 @@ USB card reader USB-уÑтройÑтво Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚ - - Found new USB card reader that is suitable for the ID card. The workflow may now be continued. - Ðайдено новое USB-уÑтройÑтво Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚, подходÑщее Ð´Ð»Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ карты. Теперь можно продолжить процеÑÑ. - TechnologySwitch @@ -5550,15 +5797,14 @@ Показать внутренние Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ Ð² приложении %1 - Title bar - LABEL DESKTOP - Строка заголовка - - Hide in-app notifications of %1 LABEL DESKTOP Скрыть внутренние Ð¾Ð¿Ð¾Ð²ÐµÑ‰ÐµÐ½Ð¸Ñ Ð² приложении %1 + + Update available + + TitleBarNavigation @@ -5572,6 +5818,11 @@ LABEL ANDROID IOS Ðазад + + Close + LABEL ANDROID IOS + Закрыть + TrayIconView @@ -5634,23 +5885,9 @@ Обновление Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ - An update information for your platform is not available. - LABEL DESKTOP Resulttext if no update information is available for the current platform. - Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾Ð± обновлении Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ платформы недоÑтупна. - - - The update information could not be retrieved. Please check your network connection. - LABEL DESKTOP Resulttext if the update information are invalid, might be caused by network issues. - Ðе удалоÑÑŒ загрузить информацию об обновлении. Проверьте Ñетевое подключение. - - - Your version %1 of %2 is up to date! - LABEL DESKTOP The currently installed version is the most recent one, no action is required. - Ваша верÑÐ¸Ñ %2 %1 ÑвлÑетÑÑ Ð°ÐºÑ‚ÑƒÐ°Ð»ÑŒÐ½Ð¾Ð¹! - - - An update is available (installed version %1) - ДоÑтупно обновление (уÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ %1) + An update for the outdated installed version (%1) is available for download. + LABEL DESKTOP %1 is replaced with the current version number + Warning - Your operating system is no longer supported @@ -5672,6 +5909,11 @@ INFO DESKTOP Header of the popup that is shown when the app download failed. Предупреждение: Ñбой Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ + + Update available + LABEL DESKTOP + + UpdateViewButtonRow @@ -5685,33 +5927,56 @@ LABEL DESKTOP Start to download the update and execute it on Windows ЗапуÑтить обновление + + Download progress + LABEL DESKTOP Name of an progress indicator during a download read by screen readers + + + + The update (version %1) is being performed... + LABEL DESKTOP %1 is replaced with the version number of the software update. + + UpdateViewInformation - New version: + New version LABEL DESKTOP Information about the available, new version number. - ÐÐ¾Ð²Ð°Ñ Ð²ÐµÑ€ÑиÑ: + ÐÐ¾Ð²Ð°Ñ Ð²ÐµÑ€ÑÐ¸Ñ - Release date: + Release date LABEL DESKTOP Date when the available update was released. - Дата выхода: + Дата выхода - Download size: + Download size LABEL DESKTOP Download size of the available update in megabyte. - Размер обновлениÑ: + Размер Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ - Download link: + Download link LABEL DESKTOP Plaintext link to the update download. - СÑылка Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸: + СÑылка Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ - Checksum link: + Checksum link LABEL DESKTOP Link to download checksum to verify the downloaded update file. - СÑылка Ð´Ð»Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ð¹ Ñуммы: + СÑылка Ð´Ð»Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÑŒÐ½Ð¾Ð¹ Ñуммы + + + + Utils + + Tap to open the following website in your browser: %1 + INFO ANDROID IOS Hint that a link is present, which will open in the browser + + + + Press space to open the following website in your browser: %1 + INFO DESKTOP Hint that a link is present, which will open in the browser + @@ -5727,14 +5992,9 @@ Опции разработчика деактивированы. - Version Information - LABEL ANDROID IOS - Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ верÑии - - - %1 more presses to toggle the advanced settings. - INFO ANDROID IOS Used in notifications when the user taps the version information - %1 нажатие (-Ñ/-й) Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð½Ð° раÑширенные наÑтройки. + %1 version + LABEL ANDROID IOS %1 is replaced with the application name + Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ верÑии Advanced settings activated. @@ -5761,6 +6021,11 @@ LABEL DESKTOP ЗаÑвление о доÑтупноÑти + + %1 more taps to toggle the advanced settings. + INFO ANDROID IOS Used in notifications when the user taps the version information + + WhiteListSurveyView @@ -5974,6 +6239,36 @@ INFO DESKTOP Text of the popup that is shown when the execution of the update failed (2/2). ЕÑли Ñто не помогает, ÑвÑжитеÑÑŒ Ñо %1Ñлужбой поддержки%2. + + Searching for software updates... + LABEL DESKTOP + + + + An update is available (version %1). + LABEL DESKTOP An update is available, the new version is supplied to the user. + ДоÑтупно обновление (верÑÐ¸Ñ %1). + + + Your version %1 of %2 is up to date. + LABEL DESKTOP %1 is replaced with the version number of the software and %2 is replaced with the application name. + Ваша верÑÐ¸Ñ %2 %1 ÑвлÑетÑÑ Ð°ÐºÑ‚ÑƒÐ°Ð»ÑŒÐ½Ð¾Ð¹. + + + An update information for your platform is not available. + LABEL DESKTOP + Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾Ð± обновлении Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ платформы недоÑтупна. + + + An update is available but retrieving the information failed. + LABEL DESKTOP The updater found an update but not all required update information are valid, this should be a very rare case. + Обновление доÑтупно, но произошел Ñбой при загрузке информации. + + + The update information could not be retrieved. Please check your network connection. + LABEL DESKTOP + Ðе удалоÑÑŒ загрузить информацию об обновлении. Проверьте Ñетевое подключение. + governikus::ApplicationModel @@ -6022,6 +6317,11 @@ LABEL ALL_PLATFORMS Ðазад к начальной Ñтранице + + Connection to ID card lost + LABEL ALL_PLATFORMS + Прервано Ñоединение Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ картой + governikus::CardInfo @@ -6076,7 +6376,7 @@ Указание органов по защите данных, ответÑтвенных за провайдера - Provider information + Provider Information LABEL ALL_PLATFORMS Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ провайдере @@ -6600,9 +6900,9 @@ Ðевозможно запуÑтить аутентификацию. Уже активен другой процеÑÑ. - The connection to the ID card has been lost. The process was aborted. + Restart the authentication process and make sure that the position of the ID card does not change during the reading process. ERROR ALL_PLATFORMS The card was removed after the PACE channel was established. - Прервано Ñоединение Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ картой. ПроцеÑÑ Ð¿Ñ€ÐµÑ€Ð²Ð°Ð½. + The authenticity of your ID card could not be confirmed. @@ -6875,11 +7175,6 @@ ВерÑÐ¸Ñ Ñмартфона, иÑпользуемого в качеÑтве уÑтройÑтва Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚ (SaC), неÑовмеÑтима Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ð¹ верÑией. УÑтановите поÑледнюю верÑию %1 на Ñмартфон и компьютер. - A timeout occurred while trying to establish a connection to the smartphone as card reader (SaC). - ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) timed out. - Тайм-аут уÑтановки ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñо Ñмартфоном, иÑпользуемым в качеÑтве уÑтройÑтва Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚ (SaC). - - An error occurred while trying to establish a connection to the smartphone as card reader (SaC). ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) failed due to network errors (Host not found, OS error, ...) Произошла ошибка при уÑтановке ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñо Ñмартфоном, иÑпользуемым в качеÑтве уÑтройÑтва Ñ‡Ñ‚ÐµÐ½Ð¸Ñ ÐºÐ°Ñ€Ñ‚ (SaC). @@ -7004,7 +7299,7 @@ - governikus::LogModel + governikus::LogFilesModel Current log LABEL ALL_PLATFORMS @@ -7015,6 +7310,9 @@ LABEL ALL_PLATFORMS Datetime format according to https://doc.qt.io/qt/qdate.html#toString and https://doc.qt.io/qt/qtime.html#toString dd.MM.yyyy, hh:mm:ss + + + governikus::LogModel The logfile is disabled. Файл журнала деактивирован. @@ -7088,14 +7386,14 @@ governikus::NumberModel - You have entered an incorrect, 6-digit Smart-eID PIN. You have <b>2 further attempts</b> to enter the correct Smart-eID PIN. - INFO ALL_PLATFORMS The wrong Smart-eID PIN was entered on the first attempt. - Ð’Ñ‹ ввели неправильный 6-значный PIN-код Ð´Ð»Ñ Smart-eID. ОÑталоÑÑŒ <b>ещё две попытки</b> ввода PIN-кода Ð´Ð»Ñ Smart-eID. + You have entered an incorrect, 6-digit Smart-eID PIN. You have%1 2 further attempts%2 to enter the correct Smart-eID PIN. + INFO ALL_PLATFORMS The wrong Smart-eID PIN was entered on the first attempt. %1 + %2 are used to emphasize. + Ð’Ñ‹ ввели неправильный 6-значный PIN-код Ð´Ð»Ñ Smart-eID. ОÑталоÑÑŒ %1ещё две попытки%2 ввода PIN-кода Ð´Ð»Ñ Smart-eID. - You have entered an incorrect, 5-digit Transport PIN 3 times, your <b>Transport PIN is now blocked</b>. To remove the block, the <b>10-digit PUK</b> must be entered first. - INFO ALL_PLATFORMS The Transport PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. - Ð’Ñ‹ трижды ввели неправильный 5-значный временный PIN-код; ваш <b>временный PIN-код заблокирован</b>. Ð”Ð»Ñ Ñ€Ð°Ð·Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñначала введите <b>10-значный PUK-код</b>. + You have entered an incorrect, 5-digit Transport PIN 3 times, your %1Transport PIN is now blocked%2. To remove the block, the%1 10-digit PUK%2 must be entered first. + INFO ALL_PLATFORMS The Transport PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. %1 + %2 are used to emphasize. + Ð’Ñ‹ трижды ввели неправильный 5-значный временный PIN-код; ваш %1временный PIN-код заблокирован%2. Ð”Ð»Ñ Ñ€Ð°Ð·Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñначала введите%1 10-значный PUK-код%2. You have entered an incorrect, 6-digit Smart-eID PIN 3 times. Your Smart-eID is now invalidated. To use a Smart-eID again you have to set one up in the guided setup on the start page. @@ -7108,9 +7406,9 @@ Ð’Ñ‹ ввели неправильный 10-значный PUK-код. Повторите попытку. - You have entered an <b>incorrect, 6-digit Smart-eID PIN 2 times</b>. After the next failed attempt you will no longer be able to use your Smart-eID and will need to set it up again. - INFO ANDROID IOS The wrong Smart-eID PIN was entered twice, a 3rd wrong attempt could invalidate the Smart-eID. - Ð’Ñ‹ дважды ввели <b>неправильный 6-значный PIN-код Ð´Ð»Ñ Smart-eID</b>. ПоÑле третьей попытки неправильного ввода вы больше не Ñможете пользоватьÑÑ Ñ„ÑƒÐ½ÐºÑ†Ð¸ÐµÐ¹ Smart-eID, и ее придетÑÑ Ð½Ð°Ñтроить еще раз. + You have entered an %1incorrect, 6-digit Smart-eID PIN 2 times%2. After the next failed attempt you will no longer be able to use your Smart-eID and will need to set it up again. + INFO ANDROID IOS The wrong Smart-eID PIN was entered twice, a 3rd wrong attempt could invalidate the Smart-eID. %1 + %2 are used to emphasize. + Ð’Ñ‹ дважды ввели %1неправильный 6-значный PIN-код Ð´Ð»Ñ Smart-eID%2. ПоÑле третьей попытки неправильного ввода вы больше не Ñможете пользоватьÑÑ Ñ„ÑƒÐ½ÐºÑ†Ð¸ÐµÐ¹ Smart-eID, и ее придетÑÑ Ð½Ð°Ñтроить еще раз. The input does not match. Please choose a new Smart-eID PIN. @@ -7124,74 +7422,74 @@ You have entered an incorrect, 6-digit ID card PIN. INFO ALL_PLATFORMS The wrong ID card PIN was entered on the first attempt. Part 1/2 - Ð’Ñ‹ ввели неправильный 6-значный PIN-код идентификационной карты. + Ð’Ñ‹ ввели неправильный 6-значный PIN-код идентификационной карты. - You have <b>2 further attempts</b> to enter the correct ID card PIN. - INFO ALL_PLATFORMS The wrong ID card PIN was entered on the first attempt. Part 2/2 - У Ð²Ð°Ñ Ð¾ÑталоÑÑŒ <b>ещё две попытки</b>, чтобы ввеÑти правильный PIN-код идентификационной карты. + You have%1 2 further attempts%2 to enter the correct ID card PIN. + INFO ALL_PLATFORMS The wrong ID card PIN was entered on the first attempt. %1 + %2 are used to emphasize. Part 2/2 + У Ð²Ð°Ñ Ð¾ÑталоÑÑŒ %1ещё две попытки%2, чтобы ввеÑти правильный PIN-код идентификационной карты. - You have entered an <b>incorrect, 6-digit ID card PIN 2 times</b>. - INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. Part 1/2 - Ð’Ñ‹ <b>дважды ввели неправильный 6-значный PIN-код идентификационной карты</b>. + You have entered an %1incorrect, 6-digit ID card PIN 2 times%2. + INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 1/2 + Ð’Ñ‹ %1дважды ввели неправильный 6-значный PIN-код идентификационной карты%2. - For a 3rd attempt, the <b>6-digit Card Access Number (CAN)</b> must be entered first. You can find your CAN in the <b>bottom right on the front of your ID card</b>. - INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. Part 2/2 + For a 3rd attempt, the%1 6-digit Card Access Number (CAN)%2 must be entered first. You can find your CAN in the %1bottom right on the front of your ID card%2. + INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 2/2 ---------- -INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. Part 2/2 - Ð”Ð»Ñ Ñ‚Ñ€ÐµÑ‚ÑŒÐµÐ¹ попытки Ñначала введите <b>6-значный код доÑтупа (CAN)</b>. Код CAN указан <b>внизу Ñправа на передней Ñтороне идентификационной карты</b>. +INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 2/2 + Ð”Ð»Ñ Ñ‚Ñ€ÐµÑ‚ÑŒÐµÐ¹ попытки Ñначала введите%1 6-значный код доÑтупа (CAN)%2. Код CAN указан %1внизу Ñправа на передней Ñтороне идентификационной карты%2. - You have entered an incorrect, 6-digit ID card PIN 3 times. Your <b>ID card PIN is now blocked</b>. + You have entered an incorrect, 6-digit ID card PIN 3 times. Your %1ID card PIN is now blocked%2. INFO ALL_PLATFORMS The ID card PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. Part 1/2 - Ð’Ñ‹ трижды ввели неправильный 6-значный PIN-код идентификационной карты. <b>PIN-код идентификационной карты заблокирован</b>. + Ð’Ñ‹ трижды ввели неправильный 6-значный PIN-код идентификационной карты. %1PIN-код идентификационной карты заблокирован%2. - To remove the block, the <b>10-digit PUK</b> must be entered first. You can find the PUK in the bottom <b>right next</b> to the Transport PIN in the <b>authority's letter</b>. + To remove the block, the%1 10-digit PUK%2 must be entered first. You can find the PUK in the bottom %1right next%2 to the Transport PIN in the %1authority's letter%2. INFO ALL_PLATFORMS The ID card PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. Part 2/2 - Ð”Ð»Ñ Ñ€Ð°Ð·Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñначала введите <b>10-значный PUK-код</b>. PUK-код указан возле временного PIN-кода <b>внизу Ñправа</b> на передней Ñтороне <b>пиÑьма компетентного органа</b>. + Ð”Ð»Ñ Ñ€Ð°Ð·Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñначала введите%1 10-значный PUK-код%2. PUK-код указан возле временного PIN-кода %1внизу Ñправа%2 на передней Ñтороне %1пиÑьма компетентного органа%2. - You have entered an <b>incorrect Card Access Number (CAN)</b>. Please try again. You can find your CAN in the <b>bottom right on the front of your ID card</b>. - INFO ALL_PLATFORMS The CAN was entered wrongfully and needs to be supplied again. - Ð’Ñ‹ ввели <b>неправильный код доÑтупа (CAN)</b>. Повторите попытку. Код CAN указан <b>внизу Ñправа на передней Ñтороне идентификационной карты</b>. + You have entered an %1incorrect Card Access Number (CAN)%2. Please try again. You can find your CAN in the %1bottom right on the front of your ID card%2. + INFO ALL_PLATFORMS The CAN was entered wrongfully and needs to be supplied again. %1 + %2 are used to emphasize. + Ð’Ñ‹ ввели %1неправильный код доÑтупа (CAN)%2. Повторите попытку. Код CAN указан %1внизу Ñправа на передней Ñтороне идентификационной карты%2. You have entered an incorrect, 5-digit Transport PIN. INFO ALL_PLATFORMS The wrong Transport PIN was entered on the first attempt. Part 1/2 - Ð’Ñ‹ ввели неправильный 5-значный временный PIN-код. + Ð’Ñ‹ ввели неправильный 5-значный временный PIN-код. - You have <b>2 further attempts</b> to enter the correct Transport PIN. The 5-digit Transport PIN may be found on the <b>bottom left of your PIN letter</b>. - INFO ALL_PLATFORMS The wrong Transport PIN was entered on the first attempt.Part 2/2 - У Ð²Ð°Ñ <b>оÑталоÑÑŒ ещё две попытки</b>, чтобы ввеÑти правильный временный PIN-код. 5-значный временный PIN код указан <b>внизу Ñлева в пиÑьме Ñ PIN-кодом</b>. + You have%1 2 further attempts%2 to enter the correct Transport PIN. The 5-digit Transport PIN may be found on the %1bottom left of your PIN letter%2. + INFO ALL_PLATFORMS The wrong Transport PIN was entered on the first attempt. %1 + %2 are used to emphasize. Part 2/2 + У Ð²Ð°Ñ %1оÑталоÑÑŒ ещё две попытки%2, чтобы ввеÑти правильный временный PIN-код. 5-значный временный PIN код указан %1внизу Ñлева в пиÑьме Ñ PIN-кодом%2. - You have entered an <b>incorrect, 5-digit Transport PIN 2 times</b>. - INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. Part 1/2 - Ð’Ñ‹ <b>дважды ввели неправильный 5-значный временный PIN-код</b>. + You have entered an %1incorrect, 5-digit Transport PIN 2 times%2. + INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 1/2 + Ð’Ñ‹ %1дважды ввели неправильный 5-значный временный PIN-код%2. - <b>An incorrect PIN has been entered 2 times</b> at the last use of your ID card. - INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. Part 1/2 - <b>PIN-код дважды введен неправильно</b> при поÑледнем иÑпользовании вашей идентификационной карты. + %1An incorrect PIN has been entered 2 times%2 at the last use of your ID card. + INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. %1 + %2 are used to emphasize. Part 1/2 + %1PIN-код дважды введен неправильно%2 при поÑледнем иÑпользовании вашей идентификационной карты. - For a 3rd attempt, the <b>6-digit Card Access Number (CAN)</b> must be entered first. You can find your CAN <b>in the bottom right on the front of your ID card</b>. - INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. Part 2/2 - Ð”Ð»Ñ Ñ‚Ñ€ÐµÑ‚ÑŒÐµÐ¹ попытки Ñначала <b>введите 6-значный код доÑтупа (CAN)</b>. Код CAN<b>указан внизу Ñправа на передней Ñтороне идентификационной карты</b>. + For a 3rd attempt, the%1 6-digit Card Access Number (CAN)%2 must be entered first. You can find your CAN %1in the bottom right on the front of your ID card%2. + INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. %1 + %2 are used to emphasize. Part 2/2 + Ð”Ð»Ñ Ñ‚Ñ€ÐµÑ‚ÑŒÐµÐ¹ попытки Ñначала %1введите 6-значный код доÑтупа (CAN)%2. Код CAN%1указан внизу Ñправа на передней Ñтороне идентификационной карты%2. - <b>An incorrect PIN has been entered 3 times</b> at the last use of your ID card. - INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. Part 1/2 - <b>PIN-код трижды введен неправильно</b> при поÑледнем иÑпользовании вашей идентификационной карты. + %1An incorrect PIN has been entered 3 times%2 at the last use of your ID card. + INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. %1 + %2 are used to emphasize. Part 1/2 + %1PIN-код трижды введен неправильно%2 при поÑледнем иÑпользовании вашей идентификационной карты. - Therefor you have to enter the <b>PUK</b> first to <b>unlock the ID card PIN</b>. - INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. Part 2/2 - Ð”Ð»Ñ Ñтого введите Ñначала <b>PUK-код</b>, чтобы <b>разблокировать PIN-код идентификационной карты</b>. + Therefore you have to enter the %1PUK%2 first to %1unlock the ID card PIN%2. + INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. %1 + %2 are used to emphasize. Part 2/2 + Ð”Ð»Ñ Ñтого введите Ñначала %1PUK-код%2, чтобы %1разблокировать PIN-код идентификационной карты%2. @@ -7380,9 +7678,9 @@ ÐедоÑтупно - Click to pair - LABEL ALL_PLATFORMS - Ðажмите Ð´Ð»Ñ ÑопрÑÐ¶ÐµÐ½Ð¸Ñ + Tap to pair + LABEL LABEL ANDROID IOS + Ðажмите Ð´Ð»Ñ ÑопрÑÐ¶ÐµÐ½Ð¸Ñ was @@ -7505,9 +7803,9 @@ Разрешение на временное проживание I - Date of expiry + Valid until LABEL ALL_PLATFORMS - ДейÑтвительно до + ДейÑтвительно до @@ -7595,6 +7893,10 @@ INFO ALL_PLATFORMS The ID card PIN was changed successfully. Ð’Ñ‹ уÑпешно изменили PIN-код Ð´Ð»Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹ карты. + + You may now remove your ID card from the device. + Можно извлечь идентификационную карту из уÑтройÑтва. + governikus::StateCheckRefreshAddress @@ -7924,5 +8226,10 @@ LABEL ALL_PLATFORMS Hint title to assist the user on how to set a new PIN УÑтановите новый PIN-код + + Error code: %1 + LABEL ALL_PLATFORMS + Код ошибки: %1 + diff -Nru ausweisapp2-2.3.1/resources/translations/ausweisapp_uk.ts ausweisapp2-2.4.0/resources/translations/ausweisapp_uk.ts --- ausweisapp2-2.3.1/resources/translations/ausweisapp_uk.ts 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/translations/ausweisapp_uk.ts 2025-10-30 10:10:48.000000000 +0000 @@ -42,9 +42,67 @@ ДоÑтупне Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ (верÑÑ–Ñ %1). - To close the app, press the back button 2 times. + To close the app, tap the back button 2 times. 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). - Щоб закрити програму, 2 рази натиÑніть кнопку «Ðазад». + Щоб закрити програму, 2 рази натиÑніть кнопку «Ðазад». + + + + AuthCanceledView + + This may take a few moments. + LABEL ALL_PLATFORMS + + + + The authentication was cancelled successfully. + LABEL ALL_PLATFORMS + + + + Back to provider + LABEL ALL_PLATFORMS + + + + Authentication was canceled + LABEL ALL_PLATFORMS + + + + The authentication is being canceled at the provider (%1) + LABEL ALL_PLATFORMS + + + + Back to start page + LABEL ALL_PLATFORMS + ПовернутиÑÑ Ð´Ð¾ початкової Ñторінки + + + The cancellation was triggered on the card reader. + LABEL ALL_PLATFORMS + + + + Back to setup + LABEL ALL_PLATFORM + ПовернутиÑÑ Ð´Ð¾ налаштувань + + + You may start a new authentication after clicking the button. + LABEL DESKTOP + + + + You may start a new authentication after tapping the button. + LABEL ANDROID IOS + + + + Cancellation progress + LABEL ALL_PLATFORMS Name of an progress indicator during the cancellation of an authentication read by screen readers + @@ -146,9 +204,14 @@ - не переміщуйте картку, поки до неї здійÑнюєтьÑÑ Ð´Ð¾Ñтуп - Network problems detected, trying to reach server within 30 seconds. - INFO DESKTOP Information message about cancellation process without working network connectivity - ВиÑвлено проблеми з мережею, виконуєтьÑÑ Ñпроба зв’ÑзатиÑÑ Ð· Ñервером протÑгом 30 Ñекунд. + Back to start page + LABEL ANDROID IOS + ПовернутиÑÑ Ð´Ð¾ початкової Ñторінки + + + Back to setup + LABEL ANDROID IOS + ПовернутиÑÑ Ð´Ð¾ налаштувань Aborting process and informing the service provider @@ -156,19 +219,16 @@ ÐŸÐµÑ€ÐµÑ€Ð¸Ð²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ†ÐµÑу й Ñ–Ð½Ñ„Ð¾Ñ€Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ñтачальника поÑлуг - Error code: %1 - INFO DESKTOP Error code (string) of current GlobalStatus code, shown as header of popup. - Код помилки: %1 - - - Back to start page - LABEL ANDROID IOS - ПовернутиÑÑ Ð´Ð¾ початкової Ñторінки + Network problems detected, trying to reach server within 30 seconds. + INFO DESKTOP Information message about cancellation process without working network connectivity + ВиÑвлено проблеми з мережею, виконуєтьÑÑ Ñпроба зв’ÑзатиÑÑ Ð· Ñервером протÑгом 30 Ñекунд. - Back to setup - LABEL ANDROID IOS - ПовернутиÑÑ Ð´Ð¾ налаштувань + Authentication progress + LABEL DESKTOP Name of an progress indicator during an authentication read by screen readers +---------- +LABEL ANDROID IOS Name of an progress indicator during an authentication read by screen readers + @@ -182,6 +242,39 @@ + AutoRedirectDecision + + Currently, the app automatically redirects you back to the service provider after authentication. This may not allow your screen reader to provide all information. To ensure you receive all information, enable manual redirection to the service provider. + INFO ALL_PLATFORMS + + + + You can change your preference at any time in the settings. + INFO ALL_PLATFORMS + + + + Skip and keep automatic redirection + LABEL ALL_PLATFORMS + + + + Enable manual redirection + INFO ALL_PLATFORMS + + + + Optimize your settings for screen reading + INFO ALL_PLATFORMS + + + + Do not display this message in future. + LABEL ALL_PLATFORMS + Ðадалі не показуйте це повідомленнÑ. + + + AutostartView Do you want to automatically start the %1 after boot? @@ -223,16 +316,11 @@ - BaseController + BaseHeading - Process finished successfully. You may now remove your ID card from the device. - INFO ALL_PLATFORMS The workflow finished successfully, the ID card may (and should) be removed from the card reader. - ÐŸÑ€Ð¾Ñ†ÐµÑ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¾ уÑпішно. Тепер ви можете видалити Ñвою ID-картку з приÑтрою. - - - You may now remove your ID card from the device. - INFO ALL_PLATFORMS The workflow is completed, the ID card may (and should) be removed from the card reader. - Тепер ви можете видалити Ñвою ID-картку з приÑтрою. + Heading + LABEL WINDOWS Screenreader announcement, that the current item is a heading. + @@ -333,6 +421,94 @@ + CardNotActivatedBaseView + + Activate ID card with PIN reset letter + LABEL ALL_PLATFORMS + + + + Request eID function activation + LABEL ALL_PLATFORMS + + + + eID function is not activated + LABEL ALL_PLATFORMS + + + + Did you recently order a PIN reset letter with an activation code? + LABEL ALL_PLATFORMS + + + + Activate your ID card using the activation code + LABEL ALL_PLATFORMS + + + + Yes, I already have an activation code + LABEL ALL_PLATFORMS + + + + Request the activation of the eID function + LABEL ALL_PLATFORMS + + + + No, I don't have an activation code + LABEL ALL_PLATFORMS + + + + Abort setup + LABEL ALL_PLATFORMS + Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ + + + Enter your activation code of your present PIN reset letter into the following website. + LABEL ALL_PLATFORMS + + + + Enter activation code + LABEL ALL_PLATFORMS + + + + Request action code + LABEL ALL_PLATFORMS + + + + You can request a PIN reset letter with an activation code on the following website. + LABEL ALL_PLATFORMS + + + + Online via PIN reset service + LABEL ALL_PLATFORMS + + + + Find competent authority + LABEL ALL_PLATFORMS + Знайти компетентний орган + + + You can activate the eID function directly at your competent authority. + LABEL ALL_PLATFORMS + + + + At your competent authority + LABEL ALL_PLATFORMS + + + + CardPositionView Retry @@ -372,7 +548,7 @@ connect your smartphone to a charger and turn off battery saver mode if necessary LABEL ANDROID IOS - підключіть Ñмартфон до зарÑдного приÑтрою Ñ– за потреби вимкніть режим економії зарÑду акумулÑтора. + підключіть Ñмартфон до зарÑдного приÑтрою Ñ– за потреби вимкніть режим економії зарÑду акумулÑтора @@ -531,7 +707,7 @@ Have your documents ready LABEL ALL_PLATFORMS - Щоб отримати готові документи, виконайте наведені нижче дії. + Щоб отримати готові документи, виконайте наведені нижче дії You need: @@ -596,7 +772,7 @@ Have your documents ready LABEL ALL_PLATFORMS - Щоб отримати готові документи, виконайте наведені нижче дії. + Щоб отримати готові документи, виконайте наведені нижче дії You need: @@ -708,9 +884,14 @@ Що це означає? - You may now try the function: "See my personal data". Press the "%1" button to do so now. - LABEL ALL_PLATFORMS - Тепер ви можете Ñпробувати ÑкориÑтатиÑÑ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ”ÑŽ: «ДивитиÑÑ Ð¼Ð¾Ñ— оÑобиÑті дані». Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ натиÑніть кнопку «%1». + You may now try the function: "See my personal data". Click the "%1" button to do so now. + LABEL DESKTOP + Тепер ви можете Ñпробувати ÑкориÑтатиÑÑ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ”ÑŽ: «ДивитиÑÑ Ð¼Ð¾Ñ— оÑобиÑті дані». Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ натиÑніть кнопку «%1». + + + You may now try the function: "See my personal data". Tap the "%1" button to do so now. + LABEL ANDROID IOS + Тепер ви можете Ñпробувати ÑкориÑтатиÑÑ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ”ÑŽ: «ДивитиÑÑ Ð¼Ð¾Ñ— оÑобиÑті дані». Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ натиÑніть кнопку «%1». NFC supported @@ -836,11 +1017,6 @@ Ðе вдалоÑÑ Ð²Ñтановити Ñтабільне Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· вашою ID-карткою.<br><br>Почніть перевірку ще раз. Спробуйте змінити Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ ÐºÐ°Ñ€Ñ‚ÐºÐ¸ та не рухайте Ñ—Ñ— під Ñ‡Ð°Ñ Ñ‚ÐµÑту.<br><br>Якщо Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· ID-карткою не вдаєтьÑÑ Ð²Ñтановити навіть у різних положеннÑÑ…, це означає, що Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ NFC вашого мобільного приÑтрою не може забезпечити ID-картку доÑтатньою потужніÑтю.<br><br>Ви можете знайти Ñмартфони, ÑуміÑні з %1, на нашому <a href="%2">Ñайті</a>. - Abort setup - LABEL ALL_PLATFORMS - Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ - - ID card PIN suspended LABEL ALL_PLATFORMS PIN-код ID-картки призупинено @@ -888,21 +1064,11 @@ Жодної підтримуваної ID-картки не було виÑвлено. Програма %1 підтримує:<p><ul><li>Ðімецькі ID-картки</li><li>Електронні поÑвідки на Ð¿Ñ€Ð¾Ð¶Ð¸Ð²Ð°Ð½Ð½Ñ (eAT)</li><li>Картки eID Ð´Ð»Ñ Ð³Ñ€Ð¾Ð¼Ð°Ð´Ñн ЄС/ЄЕЗ</li></ul></p>Якщо ви ÑкориÑталиÑÑ Ð¾Ð´Ð½Ð¸Ð¼ із вищезазначених документів, але це Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ помилку вÑе одно з’ÑвлÑєтьÑÑ, перезапуÑтіть перевірку. - eID function disabled - LABEL ALL_PLATFORMS - Функцію eID вимкнено - - - Activate the eID function. - LABEL ALL_PLATFORMS Hint when a workflow failed because the eID function was not activated - Ðктивуйте функцію eID. - - - You may continue the onboarding and change your PIN. + You may continue the setup and change your PIN. LABEL ALL_PLATFORMS Sentence 2 of 3 of CAN explanation ---------- LABEL ALL_PLATFORMS Sentence 2 of 3 of PUK explanation - Ви можете продовжити Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ– змінити PIN-код. + Ви можете продовжити Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ– змінити PIN-код. @@ -1080,24 +1246,19 @@ Перервати операцію - The user interface of the %1 is closed. + How should the %1 be closed in the future? INFO DESKTOP Header of the popup that is shown when the AA is closed for the first time. - Ð†Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ñ€Ð¸Ñтувача %1 закрито. + Ð†Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ñ€Ð¸Ñтувача %1 закрито. - The %1 is closed. - INFO DESKTOP Header of the popup that is shown when the AA is quit for the first time. - Програму %1 закрито. - - - Completely close the app + Close completely LABEL DESKTOP - ПовніÑтю закрийте програму + ПовніÑтю закрийте програму - Just close the user interface + Leave active in the background LABEL DESKTOP - Лише закрийте Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ñ€Ð¸Ñтувача + Лише закрийте Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ñ€Ð¸Ñтувача If the %1 is closed, it is no longer available for authentication. You must then restart the app to authenticate yourself to service providers. @@ -1105,44 +1266,24 @@ Якщо %1 закрито, він більше не буде доÑтупний Ð´Ð»Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ—. Щоб пройти автентифікацію у поÑтачальників поÑлуг, вам Ñлід перезапуÑтити програму. - If the %1 is terminated, it is no longer available for authentication. You must then restart the app in order to identify yourself to service providers. - INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is disabled. - Якщо %1 перервано, він більше не буде доÑтупний Ð´Ð»Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ—. Щоб ідентифікувати Ñебе у поÑтачальників поÑлуг, вам Ñлід перезапуÑтити програму. - - - The app remains available via the icon in the system tray. Click on the %1 icon to reopen the user interface. - INFO DESKTOP Content of the popup that is shown when the AA is closed and the tray icon is enabled. - Програма залишаєтьÑÑ Ð´Ð¾Ñтупною через піктограму в ÑиÑтемній панелі. ÐатиÑніть піктограму %1, щоб знову відкрити Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ñ€Ð¸Ñтувача. - - - The app remains active in the background and can be reopened via the %1 icon on the menu bar again. - INFO DESKTOP Content of the MacOS-popup that is shown when the AA is closed and the tray icon is enabled. - Програма Ñ– надалі активна у фоновому режимі Ñ– може бути знову відкрита за допомогою піктограми %1 на панелі меню. - - - The app remains active in the background and can be reopened via the %1 icon in the notification area of the Windows taskbar. - INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is enabled. - Програма Ñ– надалі активна у фоновому режимі Ñ– може бути знову відкрита за допомогою піктограми %1 в облаÑті Ñповіщень на панелі завдань Windows. - - Do not display this message in future. LABEL DESKTOP Ðадалі не показуйте це повідомленнÑ. - If you only close the user interface, the app remains active in the background in the future and can be opened again via the %1 icon on the menu bar. - INFO DESKTOP Content of the MacOS-popup that is shown when the AA is closed and the tray icon is disabled. - Якщо ви закриєте лише Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ñ€Ð¸Ñтувача, програма надалі буде активною у фоновому режимі, Ñ– Ñ—Ñ— можна буде знову відкрити за допомогою піктограми %1 на панелі меню. + If the %1 remains active in the background, it will open automatically as soon as you start an authentication. You can still open the %1 manually at any time. + INFO DESKTOP %1 is replaced with the application name + Якщо ви закриєте лише Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ñ€Ð¸Ñтувача, програма надалі буде активною у фоновому режимі, Ñ– Ñ—Ñ— можна буде знову відкрити за допомогою піктограми %1 на панелі меню. - If you only close the user interface, the app remains active in the background in the future and can be reopened via the %1 icon in the notification area of the Windows taskbar. - INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is disabled. - Якщо ви закриєте лише Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ñ€Ð¸Ñтувача, програма надалі буде активною у фоновому режимі, Ñ– Ñ—Ñ— можна буде знову відкрити за допомогою піктограми %1 в облаÑті Ñповіщень на панелі завдань Windows. + Abort process + INFO DESKTOP + - Close user interface to menu bar - LABEL DESKTOP - Закрийте Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ ÐºÐ¾Ñ€Ð¸Ñтувача на панелі меню + You can change your selection at any time in the settings. + INFO DESKTOP Note to the user that the setting is available in the settings + @@ -1167,7 +1308,7 @@ - DarkModeButtons + DarkModeButtonData System LABEL ALL_PLATFORMS @@ -1257,11 +1398,6 @@ DetachedLogView - Select log: - LABEL DESKTOP - Виберіть журнал: - - Zoom: LABEL DESKTOP ЗбільшеннÑ: @@ -1306,6 +1442,11 @@ LABEL DESKTOP Фільтр. Деактивовано. + + Current Log + LABEL DESKTOP + + DetachedLogViewWindow @@ -1389,6 +1530,16 @@ LABEL DESKTOP Режим розробника деактивує деÑкі перевірки безпеки, а Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— продовжитьÑÑ, навіть Ñкщо виникнуть помилки. Пропущені помилки будуть показані Ñк ÑповіщеннÑ. Режим розробника можна викориÑтовувати лише з теÑтовою PKI. + + Using the developer mode forces the notifications to be enabled. + LABEL DESKTOP Only visible when the user activates the developer mode in the settings. + ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ñƒ розробника примуÑово вмикає ÑповіщеннÑ. + + + Show notifications inside of %1 + LABEL DESKTOP + Показувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñередині %1 + DevicesListDelegate @@ -1715,18 +1866,8 @@ GCollapsible - collapse - LABEL ANDROID IOS - Згорнути - - - expand - LABEL ANDROID IOS - Розгорнути - - Currently selected is %1 - LABEL ANDROID IOS + LABEL ALL_PLATFORMS Ðаразі обрано %1 @@ -1741,24 +1882,25 @@ GProgressBar - %1 percent done - %1 відÑотків виконано + Progress + LABEL ALL_PLATFORMS + - GStagedProgressBar + GRadioButton - Step %1 of %2. This step is %3 percent complete. - LABEL ALL_PLATFORMS - Крок %1 з %2. Цей крок виконано на %3 відÑотків. + checked + LABEL DESKTOP + - GText + GStagedProgressBar - Press space to open link - INFO DESKTOP Text read by screen reader if the text contains a weblink which may be opened. - ÐатиÑніть пробіл, щоб відкрити поÑÐ¸Ð»Ð°Ð½Ð½Ñ + Step %1 of %2. This step is %3 percent complete. + LABEL ALL_PLATFORMS + Крок %1 з %2. Цей крок виконано на %3 відÑотків. @@ -1776,11 +1918,6 @@ Поведінка - Using the developer mode forces the notifications to be enabled. - LABEL DESKTOP Only visible when the user activates the developer mode in the settings. - ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ñƒ розробника примуÑово вмикає ÑповіщеннÑ. - - Network LABEL DESKTOP Мережа @@ -1796,7 +1933,7 @@ Зовнішній виглÑд - Use the system font + Use system font LABEL DESKTOP ВикориÑтовувати шрифт ÑиÑтеми @@ -1811,11 +1948,6 @@ Закрийте вікно %1 піÑÐ»Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— - Show notifications inside of %1 - LABEL DESKTOP - Показувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñередині %1 - - Change language LABEL DESKTOP Змінити мову @@ -1870,6 +2002,31 @@ LABEL WINDOWS Text for attaching the AA to the system tray Прикріпіть %1 до ÑиÑтемної панелі (рекомендовано) + + Automatically check for software updates at program start (recommended) + LABEL DESKTOP + + + + Show update + LABEL DESKTOP + Показати Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ + + + Start manual search for software update + LABEL DESKTOP + + + + Abort search + LABEL DESKTOP + + + + When you start %1, it automatically checks for updates. Updates are not performed automatically. If this option is disabled, you have to manually check for updates in the settings. + LABEL DESKTOP %1 is replaced with the application name + + GeneralWorkflow @@ -1883,9 +2040,9 @@ ВикориÑтовуваний приÑтрій Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº не відповідає технічним вимогам (Extended Length не підтримуєтьÑÑ). - Place ID card + Read out ID card with connected device LABEL DESKTOP - ПоміÑтіть ID-картку + Connect USB card reader or smartphone @@ -2086,6 +2243,19 @@ + LinkQualityAnimation + + Link quality unavailable. + INFO ALL_PLATFORMS + + + + %1% link quality. + INFO ALL_PLATFORMS %1 is replaced with a number between 0 and 100 + + + + LocalNetworkInfo Go to application settings @@ -2099,15 +2269,45 @@ + LogFilesView + + Select Log + LABEL ANDROID IOS + + + + Delete + LABEL ANDROID IOS + Видалити + + + All old logs will be deleted. + INFO ANDROID IOS All logfiles are about to be removed, user confirmation required. + УÑÑ– Ñтарі журнали буде видалено. + + + Delete all logs + LABEL ANDROID IOS + Видалити вÑÑ– журнали + + + LogTitleBarControls Share log + LABEL ANDROID IOS Ðадати Ñпільний доÑтуп до журналу Delete all logs + LABEL ANDROID IOS Видалити вÑÑ– журнали + + Filter + LABEL ANDROID IOS + Фільтр + LogView @@ -2136,40 +2336,20 @@ All old logs will be deleted. - INFO DESKTOP All logfiles are about to be removed, user confirmation required. ----------- -INFO ANDROID IOS All logfiles are about to be removed, user confirmation required. + INFO DESKTOP All logfiles are about to be removed, user confirmation required. УÑÑ– Ñтарі журнали буде видалено. - Log - LABEL ANDROID IOS - Журнал - - - Select log from list. - Вибрати журнал із ÑпиÑку. - - Delete all logs - LABEL DESKTOP ----------- -LABEL ANDROID IOS + LABEL DESKTOP Видалити вÑÑ– журнали Delete - LABEL DESKTOP ----------- -LABEL ANDROID IOS + LABEL DESKTOP Видалити - The log entry was copied to the clipboard. - INFO ANDROID IOS Toast message used to confirm the copy of a log entry. - Ð—Ð°Ð¿Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ñƒ Ñкопійовано до буфера обміну. - - Filter LABEL ANDROID IOS Фільтр @@ -2194,7 +2374,9 @@ LogViewDelegate The log entry was copied to the clipboard. - INFO DESKTOP Toast message used to confirm the copy of a log entry. + INFO DESKTOP Toast message used to confirm the copy of a log entry. +---------- +INFO ANDROID IOS Toast message used to confirm the copy of a log entry. Ð—Ð°Ð¿Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð»Ñƒ Ñкопійовано до буфера обміну. @@ -2275,9 +2457,9 @@ Загальні - Version information - LABEL ANDROID IOS - Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ верÑÑ–ÑŽ + %1 version + LABEL ANDROID IOS %1 is replaced with the application name + Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ верÑÑ–ÑŽ Software license @@ -2354,9 +2536,9 @@ Умови викориÑÑ‚Ð°Ð½Ð½Ñ Ñ‚Ð° Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ð½Ð° програмне Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ - Start onboarding + Start setup LABEL ANDROID IOS - Почати Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ + Почати Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ @@ -2405,14 +2587,14 @@ Контакти - Start onboarding + Start setup LABEL DESKTOP - Почати Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ + Почати Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ - Onboarding + Setup LABEL DESKTOP - ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ + ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ @@ -2483,8 +2665,10 @@ 5-значний транÑпортний PIN-код – %1це одноразовий PIN-код%2, Ñкий ви отримали у %1лиÑті%2 під Ñ‡Ð°Ñ Ð¿Ð¾Ð´Ð°Ñ‡Ñ– заÑви на Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ID-картки. - When setting up your ID card, you will %1replace%2 this 5-digit %1Transport PIN with%2 a 6-digit, %1self chosen card PIN%2. - INFO ALL_PLATFORMS Answer to the question 'Where do I find the Transport PIN?' paragraph 2/2 + When you set up your ID card, you will %1replace%2 this 5-digit %1Transport PIN with%2 a 6-digit, %1self chosen card PIN%2. + INFO ALL_PLATFORMS Answer to the question 'Where do I find the Transport PIN?' paragraph 2/2 +---------- +INFO ALL_PLATFORMS Description text explaining the PINs 3/7 Під Ñ‡Ð°Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¾Ñ— ID-картки, ви %1заміните%2 цей 5-значний %1транÑпортний PIN-код на%2 6-значний, %1вибраний вами PIN-код картки%2. @@ -2493,7 +2677,7 @@ Де Ñ Ð¼Ð¾Ð¶Ñƒ дізнатиÑÑ PUK-код картки? - The PUK is a %1 10-digit number%2 that you can find in the %1PIN letter%2 that was sent to you by mail after you %1applied for your ID card%2. You may find it to the %1right%2 of the 5-digit %1Transport PIN%2. + The PUK is a%1 10-digit number%2 that you can find in the %1PIN letter%2 that was sent to you by mail after you %1applied for your ID card%2. You may find it to the %1right%2 of the 5-digit %1Transport PIN%2. LABEL ALL_PLATFORMS INFO ALL_PLATFORMS Answer to the question 'Where do I find the PUK?' PUK-код – це %110-значний номер%2, Ñкий ви можете знайти в %1лиÑті з PIN-кодом%2, надіÑланому вам поштою піÑÐ»Ñ Ñ‚Ð¾Ð³Ð¾, Ñк ви %1подали заÑву на Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ID-картки%2. Він знаходитьÑÑ %1праворуч%2 від 5-значного %1транÑпортного PIN-коду%2. @@ -2503,7 +2687,7 @@ Ðавіщо потрібен PUK-код? - The PUK is required if the %1card PIN has been entered incorrectly 3 times%2 in a row. As a result, the card PIN is blocked. By entering the PUK you will %1unblock the card PIN%2 and have %1 3 more attempts%2 to enter the correct PIN. + The PUK is required if the %1card PIN has been entered incorrectly 3 times%2 in a row. As a result, the card PIN is blocked. By entering the PUK you will %1unblock the card PIN%2 and have%1 3 more attempts%2 to enter the correct PIN. INFO ALL_PLATFORMS Answer to the question 'Why is the PUK required?' PUK-код потрібен, Ñкщо %1PIN-код-картки було введено неправильно 3 рази%2 поÑпіль. ВнаÑлідок цього PIN-код-картки блокуєтьÑÑ. Ввівши PUK-код, ви %1розблокуєте PIN-код картки%2 Ñ– матимете %1ще 3 Ñпроби%2 ввеÑти правильний PIN-код. @@ -2645,11 +2829,6 @@ Ви можете змінювати Ñвій 6-значний PIN-код Smart-eID у будь-Ñкий Ñ‡Ð°Ñ Ñ– необмежену кількіÑть разів, Ñкщо ви знаєте Ñвій дійÑний PIN-код Smart-eID. - When you set up the eID function, you will %1replace%2 this 5-digit %1Transport PIN%2 with a 6-digit %1card PIN that you choose yourself%2. - INFO ALL_PLATFORMS Description text explaining the PINs 3/7 - Коли ви налаштуєте функцію eID, ви %1заміните%2 цей 5-значний %1транÑпортний PIN-код%2 на 6-значний %1PIN-код картки, Ñкий ви оберете ÑамоÑтійно%2. - - The Smart-eID PIN also has six digits. You also choose that PIN yourself while setting up the Smart-eID for the first time. INFO ALL_PLATFORMS Description text explaining the PINs 5/7 PIN-код Smart-eID також має шіÑть цифр. Цей PIN-код ви також обираєте ÑамоÑтійно під Ñ‡Ð°Ñ Ð¿ÐµÑ€ÑˆÐ¾Ð³Ð¾ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Smart-eID. @@ -2700,9 +2879,9 @@ У мене Ñ” лише 5-значний транÑпортний PIN-код - You need to change the %1 5-digit Transport PIN%2 to your personal card PIN. Use %1Change PIN > Transport PIN%2 from the startpage to do so. + You need to change the%1 5-digit Transport PIN%2 to your personal card PIN. Use %1Change PIN > Transport PIN%2 from the startpage to do so. INFO ALL_PLATFORMS Explanation if only the Transport PIN is at hand - Ви повинні змінити %1 5-значний транÑпортний PIN-код%2 на Ñвій влаÑний PIN-код картки. Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ ÑкориÑтайтеÑÑ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ”ÑŽ %1Змінити PIN-код > ТранÑпортний PIN-код%2 на початковій Ñторінці. + Ви повинні змінити%1 5-значний транÑпортний PIN-код%2 на Ñвій влаÑний PIN-код картки. Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ ÑкориÑтайтеÑÑ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ”ÑŽ %1Змінити PIN-код > ТранÑпортний PIN-код%2 на початковій Ñторінці. If you have forgotten your Smart-eID PIN, you can renew your Smart-eID and thereby set a new PIN. @@ -2768,7 +2947,7 @@ Якщо ви вÑтановили 6-значний PIN-код, транÑпортний PIN-код більше не дійÑний. ПіÑÐ»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ð´Ð»Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— Ñвоєї оÑоби ви можете викориÑтовувати лише 6-значний PIN-код. - The 6-digit card PIN is a %1number that you choose yourself%2 when you set up the eID function for the first time. It %1replaces%2 your %1 5-digit Transport PIN%2. + The 6-digit card PIN is a %1number that you choose yourself%2 when you set up the eID function for the first time. It %1replaces%2 your%1 5-digit Transport PIN%2. INFO ALL_PLATFORMS Description text explaining the PINs 4/7 6-значний PIN-код картки – це %1код, Ñкий ви обираєте ÑамоÑтійно%2, коли вперше налаштовуєте функцію eID. Він %1замінює%2 ваш %15-значний транÑпортний PIN-код%2. @@ -2917,12 +3096,19 @@ NavigationAction Cancel + LABEL DESKTOP СкаÑувати Back + LABEL DESKTOP Ðазад + + Close + LABEL DESKTOP + Закрийте + NavigationView @@ -3049,7 +3235,7 @@ NFC scan is not running - INFO ANDROID IOS NFC is available and enabled but needs to be started. + INFO ANDROID IOS Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ NFC не виконуєтьÑÑ @@ -3069,7 +3255,7 @@ Start scan - INFO ANDROID IOS + INFO ANDROID IOS NFC is available and enabled but needs to be started. Почати ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ @@ -3154,30 +3340,48 @@ + Notifications + + Notification: %1 + LABEL DESKTOP %1 will be replaced with a notification text + + + + NumberField The number is hidden. - LABEL DESKTOP Screenreader text for the password field + LABEL ALL_PLATFORMS Screenreader text for the password field Ðомер приховано. You entered %1 of %2 digits. - LABEL DESKTOP Screenreader text for the password field + LABEL ALL_PLATFORMS Screenreader text for the password field Ви ввели %1 з %2 знаків. - Press to hide the number + Click to hide the number LABEL DESKTOP Screenreader text for the eye icon to change the password visibility - ÐатиÑніть, щоб приховати номер + ÐатиÑніть, щоб приховати номер + + + Tap to hide the number + LABEL ANDROID IOS Screenreader text for the eye icon to change the password visibility + ÐатиÑніть, щоб приховати номер - Press to show the number + Click to show the number LABEL DESKTOP Screenreader text for the eye icon to change the password visibility - ÐатиÑніть, щоб показати номер + ÐатиÑніть, щоб показати номер + + + Tap to show the number + LABEL ANDROID IOS Screenreader text for the eye icon to change the password visibility + ÐатиÑніть, щоб показати номер The number is visible. Digits entered so far: %1 - LABEL DESKTOP Screenreader text for the password field + LABEL ALL_PLATFORMS Screenreader text for the password field Ðомер відображаєтьÑÑ. Ðаразі введені цифри: %1 @@ -3425,9 +3629,9 @@ Ðа жаль, ви не відповідаєте вÑім вимогам, необхідним Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ %1. - You may restart the setup anytime under %1Help > Onboarding%2. + You may restart the setup anytime under %1Help > Setup%2. LABEL ALL_PLATFORMS %1 and %2 are replaced with bold emphasis. - Ви будь-коли можете перезапуÑтити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñƒ розділі %1Довідка > ПідключеннÑ%2. + Ви будь-коли можете перезапуÑтити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñƒ розділі %1Довідка > ПідключеннÑ%2. The setup will now be aborted - afterwards %1one prompt regarding personalized settings in the %3%2 will follow. @@ -3545,14 +3749,9 @@ або - Afterwards, a pairing code is displayed on your smartphone. You may then select the device in the list below to enter the pairing code. - LABEL DESKTOP - ПіÑÐ»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ на вашому Ñмартфоні відобразитьÑÑ ÐºÐ¾Ð´ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸. Потім ви можете вибрати приÑтрій у ÑпиÑку нижче, щоб ввеÑти код ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸. - - - Press space to continue onboarding using the smartphone "%1" - LABEL DESKTOP - ÐатиÑніть пробіл, щоб продовжити Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð·Ð° допомогою Ñмартфона «%1». + Afterwards, a pairing code is displayed on your smartphone. You may then select the device in the %1 list to enter the pairing code. + LABEL DESKTOP %1 will be replaced with the Available Devices list name + ПіÑÐ»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ на вашому Ñмартфоні відобразитьÑÑ ÐºÐ¾Ð´ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸. Потім ви можете вибрати приÑтрій у ÑпиÑку нижче, щоб ввеÑти код ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸. Use device @@ -3630,7 +3829,9 @@ OnboardingView Continue - LABEL DESKTOP + LABEL DESKTOP +---------- +LABEL ANDROID IOS Продовжити @@ -3650,24 +3851,34 @@ Open %1 on your %2other device%3. - LABEL ANDROID IOS Assistance text for pairing new devices. Step 1 of 4 + LABEL ANDROID IOS Assistance text for pairing new devices. Step 1 of 5 Відкрийте %1 на Ñвоєму %2іншому приÑтрої%3. - On that device go to %1Settings%2 and then %1Smartphone as card reader%2 resp. %1Manage pairings%2. - LABEL ANDROID IOS Assistance text for pairing new devices. Step 2 of 4. %1 and %2 are surrounding tags for bold font. - Перейдіть на тому приÑтрої до розділу %1Параметри%2, а тоді виберіть %1Смартфон Ñк приÑтрій Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº%2 Ñ– %1ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑтвореннÑм пари%2. + Make sure that both devices are on the %1same network%2 (e.g. WiFi). + LABEL ANDROID IOS Assistance text for pairing new devices. Step 2 of 5. %1 and %2 are surrounding tags for bold font. + + + + On your other device, go to %1Settings%2 and then %1Smartphone as card reader%2 resp. %1Manage pairings%2. + LABEL ANDROID IOS Assistance text for pairing new devices. Step 3 of 5. %1 and %2 are surrounding tags for bold font. + Choose this smartphone in the list to pair it. - LABEL ANDROID IOS Assistance text for pairing new devices. Step 3 of 4 + LABEL ANDROID IOS Assistance text for pairing new devices. Step 4 of 5 Виберіть цей Ñмартфон у переліку, щоб Ñтворити з ним пару. Enter the pairing code "%1". - LABEL ANDROID IOS Provide pairing code. Step 4 of 4 + LABEL ANDROID IOS Provide pairing code. Step 5 of 5 Уведіть код ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸ «%1». + + Please follow these steps: + LABEL ANDROID IOS + + PairingFailedView @@ -3956,18 +4167,23 @@ PkiSwitch - %1 more presses to toggle the environment (prod/test) for integrated functions. + %1 more clicks to toggle the environment (prod/test) for integrated functions. + INFO DESKTOP Used in notifications when the user taps the icon + %1 більше натиÑкань Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ð½Ð½Ñ Ñередовища (продукт/теÑÑ‚) Ð´Ð»Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ… функцій. + + + %1 more taps to toggle the environment (prod/test) for integrated functions. INFO ANDROID IOS Used in notifications when the user taps the icon - %1 більше натиÑкань Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ð½Ð½Ñ Ñередовища (продукт/теÑÑ‚) Ð´Ð»Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ… функцій. + %1 більше натиÑкань Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ð½Ð½Ñ Ñередовища (продукт/теÑÑ‚) Ð´Ð»Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ… функцій. Testmode for the integrated functions activated. - INFO ANDROID IOS Used in notifications when the user taps the icon + INFO ALL_PLATFORMS Used in notifications when the user taps the icon ТеÑтовий режим Ð´Ð»Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ… функцій увімкнено. Testmode for the integrated functions deactivated. - INFO ANDROID IOS Used in notifications when the user taps the icon + INFO ALL_PLATFORMS Used in notifications when the user taps the icon ТеÑтовий режим Ð´Ð»Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ… функцій вимкнено. @@ -4014,7 +4230,7 @@ The following documents are allowed: LABEL ANDROID IOS - Дозволені документи наведені нижче. + Дозволені документи наведені нижче: german ID card @@ -4037,13 +4253,16 @@ Ви можете викориÑтовувати Ñвій %2Ñмартфон із функцією NFC%3. Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ вам потрібно буде вÑтановити %1 на цей Ñмартфон. Також, ви можете викориÑтовувати %2USB-приÑтрій Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº на вашому ПК%3. - You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a %1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, click here: - LABEL DESKTOP %1 + %2 = Bold Tags, %3 = AusweisApp ----------- -LABEL ANDROID IOS %1 + %2 = Bold Tags, %3 = AusweisApp + You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a%1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, click here: + LABEL DESKTOP %1 + %2 = Bold Tags, %3 = AusweisApp Ви отримали одноразовий PIN-код – %1транÑпортний PIN-код%2 – у лиÑті від вашого компетентного органу. Ви можете замінити його %16-значним PIN-кодом картки%2 в %3 або відділі реєÑтрації громадÑн. Якщо ви не маєте PIN-коду або ви не пам’Ñтаєте PIN-код вашої картки, натиÑніть тут: + You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a%1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, tap here: + LABEL ANDROID IOS %1 + %2 = Bold Tags, %3 = AusweisApp + Ви отримали одноразовий PIN-код – %1транÑпортний PIN-код%2 – у лиÑті від вашого компетентного органу. Ви можете замінити його %16-значним PIN-кодом картки%2 в %3 або відділі реєÑтрації громадÑн. Якщо ви не маєте PIN-коду або ви не пам’Ñтаєте PIN-код вашої картки, натиÑніть тут: + + The chip in your ID card is read using %1NFC%2. To do this, simply place the ID card on the %1back of the smartphone%2. LABEL ANDROID IOS %1 + %2 = Bold Tags Чіп вашої ID-картки зчитуєтьÑÑ Ð·Ð° допомогою %1NFC%2. Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проÑто прикладіть ID-картку до %1задньої панелі Ñмартфона%2. @@ -4097,9 +4316,9 @@ Ваші оÑобиÑті дані жодним чином не зберігаютьÑÑ Ñ‚Ð° не оброблÑютьÑÑ. Див. %1 Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ð¾Ð¼Ð¾Ñтей про те, Ñк оброблÑютьÑÑ Ð²Ð°ÑˆÑ– оÑобиÑті дані. - data privacy statement of the Federal Ministry of the Interior and Community + data privacy statement of the Federal Ministry of the Interior LABEL ALL_PLATFORMS Text of the Smart-eID html link inside of a sentence - заÑву про конфіденційніÑть даних Федерального мініÑтерÑтва внутрішніх Ñправ Ñ– родини + заÑву про конфіденційніÑть даних Федерального мініÑтерÑтва внутрішніх Ñправ Ñ– родини data privacy statement @@ -4130,9 +4349,9 @@ ПоÑтачальник - Touch for more details + Tap for more details LABEL ANDROID IOS - ТоркнітьÑÑ, щоб отримати докладніші відомоÑті + ТоркнітьÑÑ, щоб отримати докладніші відомоÑті Details about the provider @@ -4216,6 +4435,24 @@ + ReaderFoundConfirmation + + Found new smartphone as card reader that is suitable for the ID card. The workflow may now be continued. + LABEL DESKTOP + + + + Found new USB card reader that is suitable for the ID card. The workflow may now be continued. + LABEL DESKTOP + Знайдено новий USB-приÑтрій Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº, Ñкий підходить Ð´Ð»Ñ ID-картки. Тепер робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð¼Ð¾Ð¶Ð½Ð° продовжити. + + + Continue + LABEL DESKTOP + Продовжити + + + RedirectView Remove the ID card from the card reader @@ -4233,14 +4470,24 @@ Якщо у Ð²Ð°Ñ Ð²Ð¸Ð½Ð¸ÐºÐ»Ð¸ Ð·Ð°Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ ви зіткнулиÑÑ Ð· помилками під Ñ‡Ð°Ñ Ð¿Ñ€Ð¾Ñ†ÐµÑу, звернітьÑÑ Ð´Ð¾ відповідного поÑтачальника. - You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, click on the "%1" button. - INFO ALL_PLATFORMS Redirect information when automatic redirect is enabled - Через кілька Ñекунд Ð²Ð°Ñ Ð±ÑƒÐ´Ðµ автоматично перенаправлено до поÑтачальника. Якщо Ð²Ð°Ñ Ð½Ðµ буде автоматично перенаправлено, натиÑніть кнопку «%1». + You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, click the "%1" button. + INFO DESKTOP Redirect information when automatic redirect is enabled + Через кілька Ñекунд Ð²Ð°Ñ Ð±ÑƒÐ´Ðµ автоматично перенаправлено до поÑтачальника. Якщо Ð²Ð°Ñ Ð½Ðµ буде автоматично перенаправлено, натиÑніть кнопку «%1». + + + Click the button to complete the authentication and return to the provider. + INFO DESKTOP Redirect information when automatic redirect is disabled + ÐатиÑніть кнопку, щоб завершити автентифікацію Ñ– повернутиÑÑ Ð´Ð¾ поÑтачальника. - Press the button to complete the authentication and return to the provider. - INFO ALL_PLATFORMS Redirect information when automatic redirect is disabled - ÐатиÑніть кнопку, щоб завершити автентифікацію Ñ– повернутиÑÑ Ð´Ð¾ поÑтачальника. + You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, tap the "%1" button. + INFO ANDROID IOS Redirect information when automatic redirect is enabled + Через кілька Ñекунд Ð²Ð°Ñ Ð±ÑƒÐ´Ðµ автоматично перенаправлено до поÑтачальника. Якщо Ð²Ð°Ñ Ð½Ðµ буде автоматично перенаправлено, натиÑніть кнопку «%1». + + + Tap the button to complete the authentication and return to the provider. + INFO ANDROID IOS Redirect information when automatic redirect is disabled + ÐатиÑніть кнопку, щоб завершити автентифікацію Ñ– повернутиÑÑ Ð´Ð¾ поÑтачальника. Return to provider @@ -4271,28 +4518,29 @@ RemoteReaderDelegate - Smartphone named "%1". %2. - INFO DESKTOP Name and status of remote device. %1 is replaced with the name, %2 with the status - Смартфон під іменем «%1». %2. + Smartphone named "%1". + INFO DESKTOP Name of remote device. %1 is replaced with the name. + Смартфон під іменем «%1». - Press space to unpair the smartphone "%1". - INFO DESKTOP Text for activation action if the device is paired. - ÐатиÑніть пробіл, щоб ÑкаÑувати пару зі Ñмартфоном «%1». + Status: "%1". + INFO DESKTOP Status of remote device. %1 is replaced with the status. + - Press space to pair the smartphone "%1". - INFO DESKTOP Text for activation action if the device is unpaired. - ÐатиÑніть пробіл, щоб Ñтворити пару зі Ñмартфоном «%1». + Pair + LABEL DESKTOP + Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸ - Remove remote device - Видаліть віддалений приÑтрій + Unpair + LABEL DESKTOP + - Pair - LABEL DESKTOP - Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸ + %1 device "%2" + LABEL DESKTOP Text of pairing button, %1 will be Pair/Unpair and %2 is replaced with device name + @@ -4349,9 +4597,9 @@ ОÑтаннє Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ - Click to remove device + Tap to remove device LABEL ANDROID IOS - ÐатиÑніть, щоб видалити приÑтрій + ÐатиÑніть, щоб видалити приÑтрій Remove @@ -4374,9 +4622,9 @@ Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¸ - Click to pair + Tap to pair LABEL ANDROID IOS - ÐатиÑніть, щоб Ñтворити пару + ÐатиÑніть, щоб Ñтворити пару Please connect your WiFi to use another smartphone as card reader (SaC). @@ -4399,9 +4647,9 @@ Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸ з приÑтроєм… - Click to use device + Tap to use device LABEL ANDROID IOS - ÐатиÑніть, щоб викориÑтовувати приÑтрій + ÐатиÑніть, щоб викориÑтовувати приÑтрій @@ -4432,11 +4680,6 @@ Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запуÑтіть Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð½Ð° приÑтрої, з Ñким Ñтворено пару. - Pairing code: <b>%1</b> - LABEL ANDROID IOS - Код ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸: <b>%1</b> - - Enable WiFi LABEL ANDROID IOS Увімкнути Wi-Fi @@ -4487,23 +4730,13 @@ Куди вводити код ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸? - Enter the pairing code "%1" in the %2 on your other device. - INFO ANDROID IOS - Введіть код ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸ "%1" у %2 на Ñвоєму іншому приÑтрої. - - Cancel pairing LABEL ANDROID IOS СкаÑувати ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ñ€Ð¸ - Activate the card reader, this allows the paired devices to use this smartphone as a card reader. - INFO ANDROID IOS - Ðктивуйте приÑтрій Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº, що дозволить приÑтроÑм, з Ñкими Ñтворено пару, викориÑтовувати цей Ñмартфон Ñк приÑтрій Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº. - - Paired devices may use this Smartphone as a card reader now. - INFO ANDROID IOS + LABEL ANDROID IOS Тепер з’єднані приÑтрої можуть викориÑтовувати цей Ñмартфон Ñк приÑтрій Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº. @@ -4524,13 +4757,55 @@ Ви можете викориÑтовувати цей Ñмартфон Ñк приÑтрій Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº Ð´Ð»Ñ %1 на інших приÑтроÑÑ…, наприклад, на ноутбуці. Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ вам потрібно Ñпочатку Ñтворити пару між таким приÑтроєм Ñ– цим Ñмартфоном. - - - RemoteServiceWifiInfo + + No device paired + LABEL ANDROID IOS + + + + Card reader not active + LABEL ANDROID IOS + + + + Use this smartphone as a card reader for a paired device + LABEL ANDROID IOS + + + + Activate the card reader + LABEL ANDROID IOS + + Both devices have to be on the same network (e.g. WiFi). INFO ANDROID IOS The remote service is active. Hint that both devices need to be connected to the same network. - Обидва приÑтрої мають бути в одній мережі (наприклад, Wi-Fi). + Обидва приÑтрої мають бути в одній мережі (наприклад, Wi-Fi). + + + Pairing code: + LABEL ANDROID IOS + + + + This allows the paired devices to use this smartphone as a card reader. + INFO ANDROID IOS + + + + Enter the pairing code + LABEL ANDROID IOS + + + + Enter the pairing code "%1" in the %2 on your other device. Both devices have to be on the same network (e.g. WiFi). + INFO ANDROID IOS %1 is replaced with the pairing code, %2 with the name "AusweisApp" + + + + Pairing progress + LABEL ANDROID IOS Name of an progress indicator during the pairing process read by screen readers + @@ -4584,11 +4859,6 @@ ResultErrorView - Error code: - LABEL ANDROID IOS - Код помилки: - - Show Details LABEL ANDROID IOS Показати детальну інформацію @@ -4637,46 +4907,6 @@ Цифрова клавіатура - Software updates - LABEL DESKTOP - ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð½Ð¾Ð³Ð¾ Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ - - - Check for updates at program start - LABEL DESKTOP - Перевірка оновлень під Ñ‡Ð°Ñ Ð·Ð°Ð¿ÑƒÑку програми - - - Show update - LABEL DESKTOP - Показати Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ - - - Check now - LABEL DESKTOP - Перевірити зараз - - - An update is available (version %1)! - LABEL DESKTOP An update is available, the new version is supplied to the user. - ДоÑтупне Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ (верÑÑ–Ñ %1)! - - - An update is available but retrieving the information failed. - LABEL DESKTOP The updater found an update but not all required update information are valid, this should be a very rare case. - ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ñтупне, але отримати інформацію не вдалоÑÑ. - - - Your version %1 of %2 is up to date. - LABEL DESKTOP The current version is up to date, no user action is required. - Ваша верÑÑ–Ñ %1 %2 Ñ” актуальною. - - - No update information available, please check for update manually. - LABEL DESKTOP The automatic update check is disabled (or no network connection was present during app start), a manual check for update is required. - Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½ÐµÐ´Ð¾Ñтупна, перевірте наÑвніÑть Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ. - - Shuffle keys LABEL DESKTOP Перемішати клавіші @@ -4728,16 +4958,6 @@ SelfAuthenticationData - Read self-authentication data - LABEL DESKTOP Title of the self authentication result data view - Читати дані Ñамоавтентифікації - - - Successfully read data - INFO DESKTOP Status message that the self authentication successfully completed. - Дані уÑпішно прочитано - - Read data LABEL DESKTOP Title of the self authentication result data view Зчитати дані @@ -4754,6 +4974,19 @@ + SelfAuthenticationHeader + + Successfully read data. + INFO ALL_PLATFORMS Status message that the self authentication successfully completed (1/2). + Дані уÑпішно прочитано. + + + You may now remove your ID card from the device. + INFO ALL_PLATFORMS Status message that the self authentication successfully completed (2/2). + Тепер ви можете видалити Ñвою ID-картку з приÑтрою. + + + SettingsView Settings @@ -5019,9 +5252,9 @@ ДотримуйтеÑÑŒ інÑтрукцій на другому приÑтрої - Now follow the instruction for the setup on your PC/Mac. If the onboarding does not start automatically, you may find it under Help > Onboarding. + Now follow the instruction for the setup on your PC/Mac. If the setup does not start automatically, you may find it under Help > Setup. LABEL ANDROID IOS - Тепер дотримуйтеÑÑŒ інÑтрукцій з Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð° вашому ПК/Mac. Якщо Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð½Ðµ почалоÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾, ви можете знайти його в розділі Довідка > ПідключеннÑ. + Тепер дотримуйтеÑÑŒ інÑтрукцій з Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð° вашому ПК/Mac. Якщо Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð½Ðµ почалоÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾, ви можете знайти його в розділі Довідка > ПідключеннÑ. Continue with pairing @@ -5372,9 +5605,9 @@ Smart-eID не готовий - Your Smart-eID is ready for use, press "Continue" to proceed. + Your Smart-eID is ready for use, tap "Continue" to proceed. LABEL ANDROID IOS - Ваш Smart-eID готовий до викориÑтаннÑ, натиÑніть «Продовжити». + Ваш Smart-eID готовий до викориÑтаннÑ, натиÑніть «Продовжити». Please wait a moment. @@ -5460,6 +5693,19 @@ + TabbedPane + + Content of tab "%1" + LABEL DESKTOP %1 will be replaced with the title of the tab + + + + Sidebar + LABEL DESKTOP + + + + TabbedPaneDelegate %1 of %2 @@ -5471,11 +5717,16 @@ LABEL DESKTOP Вибрано вкладку + + Tab + LABEL DESKTOP + Вкладка + TabbedReaderView - Card Readers + Card readers LABEL DESKTOP ПриÑтрої Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº @@ -5487,10 +5738,6 @@ USB card reader USB-приÑтрій Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº - - Found new USB card reader that is suitable for the ID card. The workflow may now be continued. - Знайдено новий USB-приÑтрій Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº, Ñкий підходить Ð´Ð»Ñ ID-картки. Тепер робочий Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð¼Ð¾Ð¶Ð½Ð° продовжити. - TechnologySwitch @@ -5545,15 +5792,14 @@ Показувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð² програмі щодо %1 - Title bar - LABEL DESKTOP - РÑдок заголовка - - Hide in-app notifications of %1 LABEL DESKTOP Приховати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ñƒ програмі щодо %1 + + Update available + + TitleBarNavigation @@ -5567,6 +5813,11 @@ LABEL ANDROID IOS Ðазад + + Close + LABEL ANDROID IOS + Закрийте + TrayIconView @@ -5629,23 +5880,9 @@ ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð¸ - An update information for your platform is not available. - LABEL DESKTOP Resulttext if no update information is available for the current platform. - Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½ÐµÐ´Ð¾Ñтупна Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— платформи. - - - The update information could not be retrieved. Please check your network connection. - LABEL DESKTOP Resulttext if the update information are invalid, might be caused by network issues. - Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ інформацію про оновленнÑ. Перевірте Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ мережі. - - - Your version %1 of %2 is up to date! - LABEL DESKTOP The currently installed version is the most recent one, no action is required. - Ваша верÑÑ–Ñ %1 %2 Ñ” актуальною! - - - An update is available (installed version %1) - ДоÑтупне Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ (уÑтановлена верÑÑ–Ñ %1) + An update for the outdated installed version (%1) is available for download. + LABEL DESKTOP %1 is replaced with the current version number + Warning - Your operating system is no longer supported @@ -5667,6 +5904,11 @@ INFO DESKTOP Header of the popup that is shown when the app download failed. ПопередженнÑ. Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ + + Update available + LABEL DESKTOP + + UpdateViewButtonRow @@ -5680,33 +5922,56 @@ LABEL DESKTOP Start to download the update and execute it on Windows Почати Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ + + Download progress + LABEL DESKTOP Name of an progress indicator during a download read by screen readers + + + + The update (version %1) is being performed... + LABEL DESKTOP %1 is replaced with the version number of the software update. + + UpdateViewInformation - New version: + New version LABEL DESKTOP Information about the available, new version number. - Ðова верÑÑ–Ñ: + Ðова верÑÑ–Ñ - Release date: + Release date LABEL DESKTOP Date when the available update was released. - Дата випуÑку: + Дата випуÑку - Download size: + Download size LABEL DESKTOP Download size of the available update in megabyte. - Розмір завантаженнÑ: + Розмір Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ - Download link: + Download link LABEL DESKTOP Plaintext link to the update download. - ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ: + ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ - Checksum link: + Checksum link LABEL DESKTOP Link to download checksum to verify the downloaded update file. - ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° контрольну Ñуму: + ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° контрольну Ñуму + + + + Utils + + Tap to open the following website in your browser: %1 + INFO ANDROID IOS Hint that a link is present, which will open in the browser + + + + Press space to open the following website in your browser: %1 + INFO DESKTOP Hint that a link is present, which will open in the browser + @@ -5722,14 +5987,9 @@ Параметри розробника деактивовано. - Version Information - LABEL ANDROID IOS - Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ верÑÑ–ÑŽ - - - %1 more presses to toggle the advanced settings. - INFO ANDROID IOS Used in notifications when the user taps the version information - %1 більше натиÑкань Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ð½Ð½Ñ Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð¸Ñ… параметрів. + %1 version + LABEL ANDROID IOS %1 is replaced with the application name + Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ верÑÑ–ÑŽ Advanced settings activated. @@ -5756,6 +6016,11 @@ LABEL DESKTOP ЗаÑва про Ñпеціальні можливоÑті + + %1 more taps to toggle the advanced settings. + INFO ANDROID IOS Used in notifications when the user taps the version information + + WhiteListSurveyView @@ -5969,6 +6234,36 @@ INFO DESKTOP Text of the popup that is shown when the execution of the update failed (2/2). Якщо це не допомагає, звернітьÑÑ Ð´Ð¾ %1Ñлужби підтримки%2. + + Searching for software updates... + LABEL DESKTOP + + + + An update is available (version %1). + LABEL DESKTOP An update is available, the new version is supplied to the user. + ДоÑтупне Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ (верÑÑ–Ñ %1). + + + Your version %1 of %2 is up to date. + LABEL DESKTOP %1 is replaced with the version number of the software and %2 is replaced with the application name. + Ваша верÑÑ–Ñ %1 %2 Ñ” актуальною. + + + An update information for your platform is not available. + LABEL DESKTOP + Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½ÐµÐ´Ð¾Ñтупна Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— платформи. + + + An update is available but retrieving the information failed. + LABEL DESKTOP The updater found an update but not all required update information are valid, this should be a very rare case. + ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ñтупне, але отримати інформацію не вдалоÑÑ. + + + The update information could not be retrieved. Please check your network connection. + LABEL DESKTOP + Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ інформацію про оновленнÑ. Перевірте Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ мережі. + governikus::ApplicationModel @@ -6017,6 +6312,11 @@ LABEL ALL_PLATFORMS ПовернутиÑÑ Ð´Ð¾ початкової Ñторінки + + Connection to ID card lost + LABEL ALL_PLATFORMS + Перервано з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· ID-карткою + governikus::CardInfo @@ -6071,7 +6371,7 @@ Ð—Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¾Ñ€Ð³Ð°Ð½Ñ–Ð², відповідальних за поÑтачальника, Ñкі перевірÑють Ð´Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð» безпеки даних - Provider information + Provider Information LABEL ALL_PLATFORMS Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ поÑтачальника @@ -6595,9 +6895,9 @@ Ðеможливо почати автентифікацію. ÐžÐ¿ÐµÑ€Ð°Ñ†Ñ–Ñ Ð²Ð¶Ðµ активна. - The connection to the ID card has been lost. The process was aborted. + Restart the authentication process and make sure that the position of the ID card does not change during the reading process. ERROR ALL_PLATFORMS The card was removed after the PACE channel was established. - Ð—â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· ID-карткою втрачено. ÐŸÑ€Ð¾Ñ†ÐµÑ Ð±ÑƒÐ»Ð¾ перервано. + The authenticity of your ID card could not be confirmed. @@ -6870,11 +7170,6 @@ ВерÑÑ–Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ Ñмартфона Ñк приÑтрою Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº (SaC) Ñ” неÑуміÑною з локальною верÑією. УÑтановіть оÑтанню верÑÑ–ÑŽ %1 Ñк на Ñвій Ñмартфон, так Ñ– на комп’ютер. - A timeout occurred while trying to establish a connection to the smartphone as card reader (SaC). - ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) timed out. - Під Ñ‡Ð°Ñ Ñпроби вÑтановити Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ñ– Ñмартфоном Ñк приÑтроєм Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº (SaC) вичерпано Ñ‡Ð°Ñ Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ. - - An error occurred while trying to establish a connection to the smartphone as card reader (SaC). ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) failed due to network errors (Host not found, OS error, ...) СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ñпроби вÑтановити Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ñ– Ñмартфоном Ñк приÑтроєм Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ ÐºÐ°Ñ€Ñ‚Ð¾Ðº (SaC). @@ -6999,7 +7294,7 @@ - governikus::LogModel + governikus::LogFilesModel Current log LABEL ALL_PLATFORMS @@ -7010,6 +7305,9 @@ LABEL ALL_PLATFORMS Datetime format according to https://doc.qt.io/qt/qdate.html#toString and https://doc.qt.io/qt/qtime.html#toString dd.MM.yyyy hh:mm:ss + + + governikus::LogModel The logfile is disabled. Файл журналу вимкнено. @@ -7082,14 +7380,14 @@ governikus::NumberModel - You have entered an incorrect, 6-digit Smart-eID PIN. You have <b>2 further attempts</b> to enter the correct Smart-eID PIN. - INFO ALL_PLATFORMS The wrong Smart-eID PIN was entered on the first attempt. - Ви ввели неправильний 6-значний PIN-код Smart-eID. ЗалишилоÑÑ <b>ще 2 Ñпроби</b> Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ñ PIN-коду Smart-eID. + You have entered an incorrect, 6-digit Smart-eID PIN. You have%1 2 further attempts%2 to enter the correct Smart-eID PIN. + INFO ALL_PLATFORMS The wrong Smart-eID PIN was entered on the first attempt. %1 + %2 are used to emphasize. + Ви ввели неправильний 6-значний PIN-код Smart-eID. ЗалишилоÑÑ %1ще 2 Ñпроби%2 Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ñ PIN-коду Smart-eID. - You have entered an incorrect, 5-digit Transport PIN 3 times, your <b>Transport PIN is now blocked</b>. To remove the block, the <b>10-digit PUK</b> must be entered first. - INFO ALL_PLATFORMS The Transport PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. - Ви 3 рази ввели неправильний 5-значний транÑпортний PIN-код. Тепер ваш <b>транÑпортний PIN-код заблоковано</b>. Щоб видалити блокуваннÑ, потрібно Ñпочатку ввеÑти <b>10-значний PUK-код</b>. + You have entered an incorrect, 5-digit Transport PIN 3 times, your %1Transport PIN is now blocked%2. To remove the block, the%1 10-digit PUK%2 must be entered first. + INFO ALL_PLATFORMS The Transport PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. %1 + %2 are used to emphasize. + Ви 3 рази ввели неправильний 5-значний транÑпортний PIN-код. Тепер ваш %1транÑпортний PIN-код заблоковано%2. Щоб видалити блокуваннÑ, потрібно Ñпочатку ввеÑти%1 10-значний PUK-код%2. You have entered an incorrect, 6-digit Smart-eID PIN 3 times. Your Smart-eID is now invalidated. To use a Smart-eID again you have to set one up in the guided setup on the start page. @@ -7102,9 +7400,9 @@ Ви ввели неправильний 10-значний PUK-код. Повторіть Ñпробу. - You have entered an <b>incorrect, 6-digit Smart-eID PIN 2 times</b>. After the next failed attempt you will no longer be able to use your Smart-eID and will need to set it up again. - INFO ANDROID IOS The wrong Smart-eID PIN was entered twice, a 3rd wrong attempt could invalidate the Smart-eID. - Ви <b>2 рази ввели неправильний 6-значний PIN-код Smart-eID</b>. ПіÑÐ»Ñ Ð½Ð°Ñтупної невдалої Ñпроби ви більше не зможете викориÑтовувати Ñвій Smart-eID Ñ– вам потрібно буде налаштувати його знову. + You have entered an %1incorrect, 6-digit Smart-eID PIN 2 times%2. After the next failed attempt you will no longer be able to use your Smart-eID and will need to set it up again. + INFO ANDROID IOS The wrong Smart-eID PIN was entered twice, a 3rd wrong attempt could invalidate the Smart-eID. %1 + %2 are used to emphasize. + Ви%1 2 рази ввели неправильний 6-значний PIN-код Smart-eID%2. ПіÑÐ»Ñ Ð½Ð°Ñтупної невдалої Ñпроби ви більше не зможете викориÑтовувати Ñвій Smart-eID Ñ– вам потрібно буде налаштувати його знову. The input does not match. Please choose a new Smart-eID PIN. @@ -7121,36 +7419,36 @@ Ви ввели неправильний 6-значний PIN-код ID-картки. - You have <b>2 further attempts</b> to enter the correct ID card PIN. - INFO ALL_PLATFORMS The wrong ID card PIN was entered on the first attempt. Part 2/2 - ЗалишилоÑÑ <b>ще 2 Ñпроби</b> Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ñ PIN-коду ID-картки. + You have%1 2 further attempts%2 to enter the correct ID card PIN. + INFO ALL_PLATFORMS The wrong ID card PIN was entered on the first attempt. %1 + %2 are used to emphasize. Part 2/2 + ЗалишилоÑÑ %1ще 2 Ñпроби%2 Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ñ PIN-коду ID-картки. - You have entered an <b>incorrect, 6-digit ID card PIN 2 times</b>. - INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. Part 1/2 - Ви ввели <b>неправильний 6-значний PIN-код ID-картки 2 рази</b>. + You have entered an %1incorrect, 6-digit ID card PIN 2 times%2. + INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 1/2 + Ви ввели %1неправильний 6-значний PIN-код ID-картки 2 рази%2. - For a 3rd attempt, the <b>6-digit Card Access Number (CAN)</b> must be entered first. You can find your CAN in the <b>bottom right on the front of your ID card</b>. - INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. Part 2/2 + For a 3rd attempt, the%1 6-digit Card Access Number (CAN)%2 must be entered first. You can find your CAN in the %1bottom right on the front of your ID card%2. + INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 2/2 ---------- -INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. Part 2/2 - Ð”Ð»Ñ 3-Ñ— Ñпроби Ñпочатку потрібно ввеÑти <b>6-значний номер доÑтупу до картки (CAN)</b>. Ви можете знайти номер CAN у <b>нижньому правому куті на лицьовому боці Ñвоєї ID-картки</b>. +INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 2/2 + Ð”Ð»Ñ 3-Ñ— Ñпроби Ñпочатку потрібно ввеÑти % 16-значний номер доÑтупу до картки (CAN)%2. Ви можете знайти номер CAN у %1нижньому правому куті на лицьовому боці Ñвоєї ID-картки%2. - You have entered an incorrect, 6-digit ID card PIN 3 times. Your <b>ID card PIN is now blocked</b>. + You have entered an incorrect, 6-digit ID card PIN 3 times. Your %1ID card PIN is now blocked%2. INFO ALL_PLATFORMS The ID card PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. Part 1/2 - Ви ввели неправильний 6-значний PIN-код ID-картки 3 рази. <b>PIN-код вашої ID-картки заблоковано</b>. + Ви ввели неправильний 6-значний PIN-код ID-картки 3 рази. %1PIN-код вашої ID-картки заблоковано%2. - To remove the block, the <b>10-digit PUK</b> must be entered first. You can find the PUK in the bottom <b>right next</b> to the Transport PIN in the <b>authority's letter</b>. + To remove the block, the%1 10-digit PUK%2 must be entered first. You can find the PUK in the bottom %1right next%2 to the Transport PIN in the %1authority's letter%2. INFO ALL_PLATFORMS The ID card PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. Part 2/2 - Щоб видалити блокуваннÑ, потрібно Ñпочатку ввеÑти <b>10-значний PUK-код</b>. PUK-код можна знайти в <b>лиÑті від органу влади</b> в нижньому <b>правому куті порÑд</b> із транÑпортним PIN-кодом. + Щоб видалити блокуваннÑ, потрібно Ñпочатку ввеÑти %110-значний PUK-код%2. PUK-код можна знайти в %1лиÑті від органу влади%2 в нижньому %1правому куті порÑд%2 із транÑпортним PIN-кодом. - You have entered an <b>incorrect Card Access Number (CAN)</b>. Please try again. You can find your CAN in the <b>bottom right on the front of your ID card</b>. - INFO ALL_PLATFORMS The CAN was entered wrongfully and needs to be supplied again. - Ви ввели <b>неправильний номер доÑтупу до картки (CAN)</b>. Повторіть Ñпробу. Ви можете знайти номер CAN у <b>нижньому правому куті на лицьовому боці Ñвоєї ID-картки</b>. + You have entered an %1incorrect Card Access Number (CAN)%2. Please try again. You can find your CAN in the %1bottom right on the front of your ID card%2. + INFO ALL_PLATFORMS The CAN was entered wrongfully and needs to be supplied again. %1 + %2 are used to emphasize. + Ви ввели %1неправильний номер доÑтупу до картки (CAN)%2. Повторіть Ñпробу. Ви можете знайти номер CAN у %1нижньому правому куті на лицьовому боці Ñвоєї ID-картки%2. You have entered an incorrect, 5-digit Transport PIN. @@ -7158,34 +7456,34 @@ Ви ввели неправильний 5-значний транÑпортний PIN-код. - You have <b>2 further attempts</b> to enter the correct Transport PIN. The 5-digit Transport PIN may be found on the <b>bottom left of your PIN letter</b>. - INFO ALL_PLATFORMS The wrong Transport PIN was entered on the first attempt.Part 2/2 - ЗалишилоÑÑ <b>ще 2 Ñпроби</b> Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ñ Ñ‚Ñ€Ð°Ð½Ñпортного PIN-коду. 5-значний транÑпортний PIN-код можна знайти в <b>нижній лівій чаÑтині лиÑта з PIN-кодом</b>. + You have%1 2 further attempts%2 to enter the correct Transport PIN. The 5-digit Transport PIN may be found on the %1bottom left of your PIN letter%2. + INFO ALL_PLATFORMS The wrong Transport PIN was entered on the first attempt. %1 + %2 are used to emphasize. Part 2/2 + ЗалишилоÑÑ %1ще 2 Ñпроби%2 Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ñ Ñ‚Ñ€Ð°Ð½Ñпортного PIN-коду. 5-значний транÑпортний PIN-код можна знайти в %1нижній лівій чаÑтині лиÑта з PIN-кодом%2. - You have entered an <b>incorrect, 5-digit Transport PIN 2 times</b>. - INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. Part 1/2 - Ви ввели неправильний <b>5-значний транÑпортний PIN 2 рази</b>. + You have entered an %1incorrect, 5-digit Transport PIN 2 times%2. + INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 1/2 + Ви ввели неправильний%1 5-значний транÑпортний PIN 2 рази%2. - <b>An incorrect PIN has been entered 2 times</b> at the last use of your ID card. - INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. Part 1/2 - Під Ñ‡Ð°Ñ Ð¾Ñтаннього викориÑÑ‚Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¾Ñ— ID-картки <b>було 2 рази введено неправильний PIN-код</b>. + %1An incorrect PIN has been entered 2 times%2 at the last use of your ID card. + INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. %1 + %2 are used to emphasize. Part 1/2 + Під Ñ‡Ð°Ñ Ð¾Ñтаннього викориÑÑ‚Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¾Ñ— ID-картки %1було 2 рази введено неправильний PIN-код%2. - For a 3rd attempt, the <b>6-digit Card Access Number (CAN)</b> must be entered first. You can find your CAN <b>in the bottom right on the front of your ID card</b>. - INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. Part 2/2 - Ð”Ð»Ñ 3-Ñ— Ñпроби Ñпочатку потрібно ввеÑти <b>6-значний номер доÑтупу до картки (CAN)</b>. Ви можете знайти номер CAN <b>у нижньому правому куті на лицьовому боці Ñвоєї ID-картки</b>. + For a 3rd attempt, the%1 6-digit Card Access Number (CAN)%2 must be entered first. You can find your CAN %1in the bottom right on the front of your ID card%2. + INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. %1 + %2 are used to emphasize. Part 2/2 + Ð”Ð»Ñ 3-Ñ— Ñпроби Ñпочатку потрібно ввеÑти%1 6-значний номер доÑтупу до картки (CAN)%2. Ви можете знайти номер CAN %1у нижньому правому куті на лицьовому боці Ñвоєї ID-картки%2. - <b>An incorrect PIN has been entered 3 times</b> at the last use of your ID card. - INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. Part 1/2 - Під Ñ‡Ð°Ñ Ð¾Ñтаннього викориÑÑ‚Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¾Ñ— ID-картки <b>було 3 рази введено неправильний PIN-код</b>. + %1An incorrect PIN has been entered 3 times%2 at the last use of your ID card. + INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. %1 + %2 are used to emphasize. Part 1/2 + Під Ñ‡Ð°Ñ Ð¾Ñтаннього викориÑÑ‚Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¾Ñ— ID-картки %1було 3 рази введено неправильний PIN-код%2. - Therefor you have to enter the <b>PUK</b> first to <b>unlock the ID card PIN</b>. - INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. Part 2/2 - Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ вам потрібно Ñпочатку ввеÑти <b>PUK-код</b>, щоб <b>розблокувати PIN-код ID-картки</b>. + Therefore you have to enter the %1PUK%2 first to %1unlock the ID card PIN%2. + INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. %1 + %2 are used to emphasize. Part 2/2 + Ð”Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ вам потрібно Ñпочатку ввеÑти %1PUK-код%2, щоб %1розблокувати PIN-код ID-картки%2. @@ -7374,9 +7672,9 @@ ÐедоÑтупно - Click to pair - LABEL ALL_PLATFORMS - ÐатиÑніть, щоб Ñтворити пару + Tap to pair + LABEL LABEL ANDROID IOS + ÐатиÑніть, щоб Ñтворити пару was @@ -7498,9 +7796,9 @@ ПоÑвідка на Ð¿Ñ€Ð¾Ð¶Ð¸Ð²Ð°Ð½Ð½Ñ I - Date of expiry + Valid until LABEL ALL_PLATFORMS - ДійÑна до + ДійÑна до @@ -7588,6 +7886,10 @@ INFO ALL_PLATFORMS The ID card PIN was changed successfully. Ви уÑпішно змінили PIN-код ID-картки. + + You may now remove your ID card from the device. + Тепер ви можете видалити Ñвою ID-картку з приÑтрою. + governikus::StateCheckRefreshAddress @@ -7800,7 +8102,7 @@ An unknown program uses the required port (%1). Please exit the other program and try again! ERROR ALL_PLATFORMS An unknown program is using the local port on which the AA listens. - Ðевідома програма викориÑтовує потрібний порт (%1). Вийдіть з іншої програми й повторіть Ñпробу. + Ðевідома програма викориÑтовує потрібний порт (%1). Вийдіть з іншої програми й повторіть Ñпробу! The program (%1) uses the required port (%2). Please close %1 and try again! @@ -7917,5 +8219,10 @@ LABEL ALL_PLATFORMS Hint title to assist the user on how to set a new PIN УÑтановити новий PIN-код + + Error code: %1 + LABEL ALL_PLATFORMS + Код помилки: %1 + diff -Nru ausweisapp2-2.3.1/resources/updatable-files/supported-providers.json ausweisapp2-2.4.0/resources/updatable-files/supported-providers.json --- ausweisapp2-2.3.1/resources/updatable-files/supported-providers.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/updatable-files/supported-providers.json 2025-10-30 10:10:48.000000000 +0000 @@ -139,7 +139,7 @@ "homepage": "https://www.bitkasten.de", "phone": "+49 911 6099 8688", "email": "nachricht@bitkasten.de", - "postalAddress": "bitkasten GmbH
Wallensteinstr. 63
90431 Nürnberg", + "postalAddress": "bitkasten GmbH
Frankenstraße 152
90461 Nürnberg", "image": "bitkasten_image.png", "icon": "bitkasten_icon.png", "category": "other" @@ -409,24 +409,6 @@ "category": "other" }, { - "eidSupport": false, - "shortName": { - "": "Bundestag ePetition" - }, - "longName": { - "": "Bundestag ePetition" - }, - "longDescription": { - "": "Mit der Online-Ausweisfunktion des Personalausweises können Sie sich einfach und sicher am Petitionsportal des Deutschen Bundestages
  • registrieren,
  • eine Petition einreichen sowie
  • eine Petition mitzeichnen.
" - }, - "address": "https://epetitionen.bundestag.de/epet/anmelden.html", - "homepage": "https://www.bundestag.de", - "phone": "+49 30 227 35257", - "email": "post.pet@bundestag.de", - "postalAddress": "Sekretariat des Petitionsausschusses
Platz der Republik 1
11011 Berlin", - "category": "citizen" - }, - { "shortName": { "": "Bürgerantrag Bremen" }, @@ -679,20 +661,21 @@ }, { "shortName": { - "": "Stadt Fürth" + "": "Digitale Bürgerdienste der Stadt Fürth" }, "longName": { - "": "Stadt Fürth" + "": "Digitale Bürgerdienste der Stadt Fürth" }, "longDescription": { - "": "Digitale Dienstleistungen der Stadt Fürth." + "": "Im Serviceportal der Stadt Fürth können Sie immer mehr Verwaltungsleistungen bequem online erledigen. Um diese vollständig digital nutzen zu können, benötigen Sie eine BayernID oder BundID. Diese können Sie schnell und einfach mit Ihrem Online-Ausweis erstellen, sodass Sie alle Vorteile des digitalen Verwaltungszugangs nutzen können.
Auf der Webseite der Stadt Fürth können Sie mit Hilfe der Online-Ausweisfunktion zum Beispiel folgende Verwaltungsleistungen digital abwickeln. Viele weitere Leistungen sind teilweise auch ohne Online-Ausweis verfügbar.
  • Digitale Wohnsitzanmeldung
  • Meldebescheinigung beantragen
  • Waffenrechtliche Erlaubnisse sowie Waffenschein beantragen
  • Aufenthaltstitel beantragen
  • Verpflichtungserklärung beantragen
  • Abfallbehälter bestellen oder abmelden
  • Gaststättenerlaubnis beantragen
  • Fahrerlaubnis beantragen
  • Führerschein Umtausch oder Änderungen durchführen
  • Fahrzeug online zulassen
  • Einbürgerung online
  • Bauantrag online
Viele weitere Leistungen sind teilweise auch ohne Online-Ausweis direkt verfügbar wie zum Beispiel die Bestellung von Geburts- oder Eheurkunden, Gewerbeanmeldung, Bewohnerparkausweise, Sperrmüll, Hunde an- oder abmelden und über 120 weitere Leistungen." }, "address": "https://www.fuerth.de/Home/edienste/online-anwendungen.aspx", "homepage": "https://www.fuerth.de", "phone": "+49 911 974 0", - "email": "poststelle@fuerth.de", + "email": "digitalisierung@fuerth.de", "postalAddress": "Stadt Fürth
Königstraße 88
90762 Fürth", "image": "StadtFuerth_image.jpg", + "icon": "StadtFuerth_icon.png", "category": "citizen" }, { @@ -714,6 +697,25 @@ "category": "citizen" }, { + "shortName": { + "": "DAKAPO" + }, + "longName": { + "": "Digitales Auskunfts- und Katasterportal Brandenburg" + }, + "longDescription": { + "": "Das Digitale Auskunfts- und Katasterportal im Land Brandenburg (DAKAPO) richtet sich an Nutzende, die hin und wieder vor allem im privaten Umfeld amtliche Auszüge aus dem Amtlichen Liegenschaftskatasterinformationssystem (ALKIS) zur Vorlage bei Ämtern, Notariaten, Gerichten oder Banken benötigen.
Die Auszüge sind kostenpflichtig und können digital als PDF-Datei oder / und analog als Ausdruck per Postversand übermittelt werden. Die Bezahlung kann per PayPal, Kreditkarte oder per Überweisung erfolgen.
Es ist ein Nutzerkonto bei BundID erforderlich, worüber die Anmeldung und die Auslieferung der Auszüge sowie des Gebührenbescheides erfolgt.
Für den Erhalt von Auszügen mit Eigentümerinformationen ist die Darlegung des berechtigten Interesses erforderlich. Dessen Prüfung unterbricht zunächst den Bestellvorgang. Dieser wird im Anschluss fortgesetzt.
Die Einsicht in das Liegenschaftskataster ist kostenfrei möglich." + }, + "address": "https://dakapo.brandenburg.de", + "homepage": "https://geobasis-bb.de", + "phone": "+49 331 8844 123", + "email": "kundenservice@geobasis-bb.de", + "postalAddress": "LGB (Landesvermessung und Geobasisinformation Brandenburg)
Heinrich-Mann-Allee 104 B
14473 Potsdam", + "image": "DAKAPO_image.jpg", + "icon": "DAKAPO_icon.png", + "category": "citizen" + }, + { "eidSupport": false, "shortName": { "": "eAntrag der Investitionsbank Berlin (IBB)" @@ -837,6 +839,24 @@ }, { "shortName": { + "": "Holvi" + }, + "longName": { + "": "Holvi" + }, + "longDescription": { + "": "Smartes Banking für Selbstständige und kleine Unternehmen: Geschäftskonto, Zahlungskarten, Rechnungen, Investments und Buchhaltungstools, alles in einem Konto." + }, + "address": "https://www.holvi.com/de", + "homepage": "https://www.holvi.com/de", + "email": "support@holvi.com", + "postalAddress": "Holvi Payment Services Oy
Zweigniederlassung Deutschland
Hasenheide 54
10967 Berlin", + "category": "finance", + "image": "Holvi_image.png", + "icon": "Holvi_icon.png" + }, + { + "shortName": { "": "Landesportal Brandenburg" }, "longName": { @@ -1174,8 +1194,8 @@ "longDescription": { "": "Gemeinschaftsserviceportal der Gemeinden des Landkreises Hameln-Pyrmont.

Die Städte- und Gemeinden des Landkreises Hameln-Pyrmont haben sich zusammengeschlossen und bieten, gemeinsam mit der Kreisverwaltung, auf Ihrem Serviceportal eine Reihe von Online-Diensten an. Städte- und Gemeinden des Landkreises Hameln-Pyrmont:
  • Aerzen
  • Bad Münder
  • Emmerthal
  • Coppenbrügge
  • Hameln
  • Bad Pyrmont
  • Hessisch Oldendorf
  • Salzhemmendorf
" }, - "address": "https://service.einfach-digital-leben.de", - "homepage": "https://service.einfach-digital-leben.de", + "address": "https://service.hameln-pyrmont.de", + "homepage": "https://service.hameln-pyrmont.de", "phone": "+49 5151 903 0", "email": "digitalisierung@hameln-pyrmont.de", "postalAddress": "Landkreis Hameln-Pyrmont
Süntelstraße 9
31785 Hameln", @@ -1193,7 +1213,7 @@ "longDescription": { "": "Das Portal 'Kfz-Online Berlin' bündelt alle Vorgänge rund um Kfz, die sich im Land Berlin online erledigen lassen." }, - "address": "https://kfz-portal.berlin.de", + "address": "https://www.behoerden-serviceportal.de/onlineantraege", "homepage": "https://www.berlin.de/labo", "phone": "+49 30 90269 0", "email": "kundenservice@berlin.de", @@ -2375,7 +2395,7 @@ "longDescription": { "": " Die BundID bietet Ihnen ein zentrales Konto zur Identifizierung für alle Ihre Online-Anträge (z. B. mit einem Online-Ausweis). Sie können das Formular Ihres Online-Antrags durch das Hinterlegen Ihrer persönlichen Daten vorausfüllen lassen. Das spart Zeit, ist sicher und bewahrt Sie vor Tippfehlern.

Sie erhalten alle Bescheide und Nachrichten in Bezug auf den Online-Antrag bequem in Ihr elektronisches Postfach in Ihrem BundID-Konto." }, - "postalAddress": "Bundesministerium des Innern und für Heimat
Alt-Moabit 140
10557 Berlin", + "postalAddress": "Bundesministerium des Innern
Alt-Moabit 140
10557 Berlin", "category": "citizen" }, { diff -Nru ausweisapp2-2.3.1/resources/updatable-files/supported-readers.json ausweisapp2-2.4.0/resources/updatable-files/supported-readers.json --- ausweisapp2-2.3.1/resources/updatable-files/supported-readers.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/resources/updatable-files/supported-readers.json 2025-10-30 10:10:48.000000000 +0000 @@ -554,7 +554,7 @@ "os": "unknown" } ], - "URL": "https://support.identiv.com/scl010-scl011" + "URL": "https://www.scm-pc-card.de/treiber" } ], "Information": [ @@ -566,8 +566,8 @@ "os": "win" } ], - "DE": "Es ist notwendig, die Treiber vom Hersteller zu installieren. Dazu folgen Sie bitte dem jeweiligen Link für Ihr Betriebssystem zur Herstellerseite. Bitte beachten Sie bei der Auswahl, dass nur der \"Windows 10 Drivers (SCL011 nPA)\" empfohlen wird.", - "EN": "It is necessary to install the drivers from the manufacturer. Please follow the relevant link for your operating system. Please note that only the driver named \"Windows 10 Drivers (SCL011 nPA)\" is advised for this card reader." + "DE": "Es ist notwendig, die Treiber vom Hersteller zu installieren. Dazu folgen Sie bitte dem jeweiligen Link für Ihr Betriebssystem zur Herstellerseite. Bitte beachten Sie bei der Auswahl, dass nur die \"nPA\" Versionen empfohlen werden.", + "EN": "It is necessary to install the drivers from the manufacturer. Please follow the relevant link for your operating system. Please note that only the \"nPA\" driver versions are advised for this card reader." }, { "Platforms": [ diff -Nru ausweisapp2-2.3.1/ruff.toml ausweisapp2-2.4.0/ruff.toml --- ausweisapp2-2.3.1/ruff.toml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/ruff.toml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,18 @@ +exclude = [ + ".git", + ".git-rewrite", + ".hg", + ".ruff_cache", + "patch.py", +] + +line-length = 79 +indent-width = 4 + +[lint] + +[format] +quote-style = "single" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "lf" diff -Nru ausweisapp2-2.3.1/src/CMakeLists.txt ausweisapp2-2.4.0/src/CMakeLists.txt --- ausweisapp2-2.3.1/src/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -2,8 +2,8 @@ # The main component that will link all necessary modules and plugins # into AusweisApp executable for the specific platform. # -# This component includes a main entry point and command line -# parser only. Everything else will be included from sub-modules. +# This component includes a main entry point only. +# Everything else will be included from sub-modules. ##################################################################### add_subdirectory(external) @@ -93,9 +93,7 @@ endif() target_link_libraries(AusweisAppBinary PRIVATE AusweisAppInit) -if(ANDROID AND INTEGRATED_SDK) - set_target_properties(AusweisAppBinary PROPERTIES OUTPUT_NAME "${PROJECT_NAME}_${CMAKE_ANDROID_ARCH_ABI}") -elseif(IOS AND INTEGRATED_SDK) +if(IOS AND INTEGRATED_SDK) set_target_properties(AusweisAppBinary PROPERTIES OUTPUT_NAME "${PROJECT_NAME}2") else() set_target_properties(AusweisAppBinary PROPERTIES OUTPUT_NAME "${PROJECT_NAME}") @@ -105,8 +103,6 @@ target_link_libraries(AusweisAppBinary PRIVATE AusweisAppConfig) endif() -ADD_SHADERS_TO_TARGET(AusweisAppBinary) - if(APPLE) set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}) set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}) diff -Nru ausweisapp2-2.3.1/src/android/MainActivity.java ausweisapp2-2.4.0/src/android/MainActivity.java --- ausweisapp2-2.3.1/src/android/MainActivity.java 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/android/MainActivity.java 2025-10-30 10:10:48.000000000 +0000 @@ -345,6 +345,7 @@ { WindowInsetsController controller = getWindow().getInsetsController(); controller.setSystemBarsAppearance(enable ? WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS : 0, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS); + controller.setSystemBarsAppearance(enable ? WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS : 0, WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS); } else { diff -Nru ausweisapp2-2.3.1/src/card/base/CardConnection.cpp ausweisapp2-2.4.0/src/card/base/CardConnection.cpp --- ausweisapp2-2.3.1/src/card/base/CardConnection.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/CardConnection.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,8 +5,19 @@ #include "CardConnection.h" #include "CardConnectionWorker.h" + using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(card) + + +const QLoggingCategory& CardConnection::getLoggingCategory() +{ + return card(); +} + + CardConnection::CardConnection(const QSharedPointer& pCardConnectionWorker) : QObject() , mCardConnectionWorker(pCardConnectionWorker) @@ -50,31 +61,31 @@ void CardConnection::setKeepAlive(bool pEnabled) { - QMetaObject::invokeMethod(mCardConnectionWorker.data(), [this, pEnabled] { - mCardConnectionWorker->setKeepAlive(pEnabled); + QMetaObject::invokeMethod(mCardConnectionWorker.data(), [worker = mCardConnectionWorker, pEnabled] { + worker->setKeepAlive(pEnabled); }); } void CardConnection::setProgressMessage(const QString& pMessage, int pProgress) { - QMetaObject::invokeMethod(mCardConnectionWorker.data(), [this, pMessage, pProgress] { - mCardConnectionWorker->setProgressMessage(pMessage, pProgress); - }, Qt::BlockingQueuedConnection); + QMetaObject::invokeMethod(mCardConnectionWorker.data(), [worker = mCardConnectionWorker, pMessage, pProgress] { + worker->setProgressMessage(pMessage, pProgress); + }); } void CardConnection::setErrorMessage(const QString& pMessage) { - QMetaObject::invokeMethod(mCardConnectionWorker.data(), [this, pMessage] { - mCardConnectionWorker->setErrorMessage(pMessage); - }, Qt::BlockingQueuedConnection); + QMetaObject::invokeMethod(mCardConnectionWorker.data(), [worker = mCardConnectionWorker, pMessage] { + worker->setErrorMessage(pMessage); + }); } -UpdateRetryCounterCommand* CardConnection::createUpdateRetryCounterCommand() +UpdateRetryCounterCommand* CardConnection::createUpdateRetryCounterCommand(const QString& pSlotHandle) { - return new UpdateRetryCounterCommand(mCardConnectionWorker); + return new UpdateRetryCounterCommand(mCardConnectionWorker, pSlotHandle); } diff -Nru ausweisapp2-2.3.1/src/card/base/CardConnection.h ausweisapp2-2.4.0/src/card/base/CardConnection.h --- ausweisapp2-2.3.1/src/card/base/CardConnection.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/CardConnection.h 2025-10-30 10:10:48.000000000 +0000 @@ -22,6 +22,7 @@ #include "command/UpdateRetryCounterCommand.h" #include +#include class test_CardConnection; @@ -37,6 +38,8 @@ friend class ::test_CardConnection; private: + static const QLoggingCategory& getLoggingCategory(); + /*! * The connection worker talks to the Card held by the Reader. */ @@ -47,7 +50,7 @@ bool mPacePinSuccessful; TransmitCommand* createTransmitCommand(const QList& pInputApduInfos, const QString& pSlotHandle); - UpdateRetryCounterCommand* createUpdateRetryCounterCommand(); + UpdateRetryCounterCommand* createUpdateRetryCounterCommand(const QString& pSlotHandle); ResetRetryCounterCommand* createResetRetryCounterCommand(); EstablishPaceChannelCommand* createEstablishPaceChannelCommand(PacePasswordId pPacePasswordId, const QByteArray& pPacePassword, const QByteArray& pEffectiveChat, const QByteArray& pCertificateDescription); @@ -74,7 +77,7 @@ } else { - qCCritical(card) << "Cannot invoke card command:" << pCommand->metaObject()->className(); + qCCritical(getLoggingCategory()) << "Cannot invoke card command:" << pCommand->metaObject()->className(); pCommand->deleteLater(); } @@ -187,9 +190,10 @@ template - QMetaObject::Connection callUpdateRetryCounterCommand(const typename QtPrivate::FunctionPointer::Object* pReceiver, T pFunc) + QMetaObject::Connection callUpdateRetryCounterCommand(const typename QtPrivate::FunctionPointer::Object* pReceiver, T pFunc, + const QString& pSlotHandle = QString()) { - auto command = createUpdateRetryCounterCommand(); + auto command = createUpdateRetryCounterCommand(pSlotHandle); return call(command, pReceiver, pFunc); } diff -Nru ausweisapp2-2.3.1/src/card/base/CardConnectionWorker.cpp ausweisapp2-2.4.0/src/card/base/CardConnectionWorker.cpp --- ausweisapp2-2.3.1/src/card/base/CardConnectionWorker.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/CardConnectionWorker.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -194,7 +194,7 @@ result.mResponseApdu = mSecureMessaging->decrypt(result.mResponseApdu); if (result.mResponseApdu.isEmpty()) { - qCDebug(::card) << "Stopping Secure Messaging since it failed. The channel therefore must not be re-used."; + qCDebug(::card) << "Stopping Secure Messaging since it failed. The channel therefore must not be reused."; stopSecureMessaging(); return {CardReturnCode::COMMAND_FAILED}; @@ -328,6 +328,29 @@ const bool isTransportPin = (pPasswordValue == QByteArray(5, 0)); Q_ASSERT(pPasswordValue.isNull() || isTransportPin); output = card->establishPaceChannel(pPasswordId, isTransportPin ? 5 : 6, pChat, pCertificateDescription); + + if (mReader->getReaderInfo().getPluginType() == ReaderManagerPluginType::PCSC + && output.getPaceReturnCode() == CardReturnCode::INVALID_PASSWORD + && pPasswordId == PacePasswordId::PACE_PIN + && output.getStatusCodeMseSetAt() == StatusCode::UNKNOWN) + { + qCWarning(::card) << "Add missing StatusCodeMseSet in EstablishPaceChannelOutput for Reiner SCT reader with pin pad" + " that do not follow PCSC Part 10 IFDs with Secure PIN Entry Capabilities - AMENDMENT 1.1"; + switch (mReader->getReaderInfo().getRetryCounter()) + { + case 2: + output.setStatusMseSetAt(QByteArray::fromHex("63C2")); + break; + + case 1: + output.setStatusMseSetAt(QByteArray::fromHex("63C1")); + break; + + default: + output.setStatusMseSetAt(QByteArray::fromHex("9000")); + + } + } } if (output.getPaceReturnCode() == CardReturnCode::INVALID_PASSWORD) diff -Nru ausweisapp2-2.3.1/src/card/base/CardInfo.h ausweisapp2-2.4.0/src/card/base/CardInfo.h --- ausweisapp2-2.3.1/src/card/base/CardInfo.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/CardInfo.h 2025-10-30 10:10:48.000000000 +0000 @@ -40,7 +40,7 @@ static const int UNDEFINED_RETRY_COUNTER; public: - CardInfo(CardType pCardType, const FileRef& pApplication = FileRef(), const QSharedPointer& = QSharedPointer(), + explicit CardInfo(CardType pCardType, const FileRef& pApplication = FileRef(), const QSharedPointer& = QSharedPointer(), int pRetryCounter = UNDEFINED_RETRY_COUNTER, bool pPinDeactivated = false, bool pPukInoperative = false, bool pPinInitial = false); void setCardType(CardType pCardType); diff -Nru ausweisapp2-2.3.1/src/card/base/FileRef.h ausweisapp2-2.4.0/src/card/base/FileRef.h --- ausweisapp2-2.3.1/src/card/base/FileRef.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/FileRef.h 2025-10-30 10:10:48.000000000 +0000 @@ -57,6 +57,16 @@ } +#if __cplusplus < 202002L +inline bool operator!=(const FileRef& pFileRefA, const FileRef& pFileRefB) +{ + return !(pFileRefA == pFileRefB); +} + + +#endif + + inline QDebug operator<<(QDebug pDbg, const FileRef& pFileRef) { QDebugStateSaver saver(pDbg); diff -Nru ausweisapp2-2.3.1/src/card/base/ReaderFilter.cpp ausweisapp2-2.4.0/src/card/base/ReaderFilter.cpp --- ausweisapp2-2.3.1/src/card/base/ReaderFilter.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/ReaderFilter.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,8 +6,6 @@ #include "ReaderConfigurationInfo.h" -#include - using namespace governikus; ReaderFilter::ReaderFilter() @@ -37,34 +35,24 @@ if (mFilterType & PluginTypeFilter) { - QMutableListIterator iter(filtered); - while (iter.hasNext()) - { - const ReaderInfo entry = iter.next(); - if (!mPluginTypes.contains(entry.getPluginType())) - { - iter.remove(); - } - } + erase_if(filtered, [this](const ReaderInfo& pEntry) { + return !mPluginTypes.contains(pEntry.getPluginType()); + }); } if (mFilterType & UniqueReaderTypes) { QList alreadyContained; - QMutableListIterator iter(filtered); - while (iter.hasNext()) - { - const ReaderConfigurationInfo configurationInfo = iter.next().getReaderConfigurationInfo(); - if (alreadyContained.contains(configurationInfo)) - { - iter.remove(); - } - else - { - alreadyContained += configurationInfo; - } - } + erase_if(filtered, [&alreadyContained](const ReaderInfo& pEntry) { + const auto configurationInfo = pEntry.getReaderConfigurationInfo(); + if (alreadyContained.contains(configurationInfo)) + { + return true; + } + alreadyContained += configurationInfo; + return false; + }); } return filtered; diff -Nru ausweisapp2-2.3.1/src/card/base/ReaderInfo.cpp ausweisapp2-2.4.0/src/card/base/ReaderInfo.cpp --- ausweisapp2-2.3.1/src/card/base/ReaderInfo.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/ReaderInfo.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,6 +5,7 @@ #include "ReaderInfo.h" #include "Initializer.h" +#include "SmartCardDefinitions.h" #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) #include "ReaderDetector.h" @@ -47,6 +48,104 @@ } +[[nodiscard]] ReaderManagerPluginType ReaderInfo::getPluginType() const +{ + return mPluginType; +} + + +[[nodiscard]] bool ReaderInfo::isValid() const +{ + return mPluginType != ReaderManagerPluginType::UNKNOWN; +} + + +void ReaderInfo::invalidate() +{ + mPluginType = ReaderManagerPluginType::UNKNOWN; + mCardInfo = CardInfo(CardType::NONE); +} + + +[[nodiscard]] CardInfo& ReaderInfo::getCardInfo() +{ + return mCardInfo; +} + + +[[nodiscard]] const CardInfo& ReaderInfo::getCardInfo() const +{ + return mCardInfo; +} + + +[[nodiscard]] CardType ReaderInfo::getCardType() const +{ + return mCardInfo.getCardType(); +} + + +[[nodiscard]] QString ReaderInfo::getCardTypeString() const +{ + return mCardInfo.getCardTypeString(); +} + + +[[nodiscard]] bool ReaderInfo::hasCard() const +{ + return mCardInfo.getCardType() != CardType::NONE; +} + + +[[nodiscard]] bool ReaderInfo::ReaderInfo::hasEid() const +{ + return QList({CardType::EID_CARD, CardType::SMART_EID}).contains(mCardInfo.getCardType()); +} + + +[[nodiscard]] int ReaderInfo::getRetryCounter() const +{ + return mCardInfo.getRetryCounter(); +} + + +[[nodiscard]] bool ReaderInfo::isRetryCounterDetermined() const +{ + return mCardInfo.isRetryCounterDetermined(); +} + + +[[nodiscard]] bool ReaderInfo::isPinDeactivated() const +{ + return mCardInfo.isPinDeactivated(); +} + + +[[nodiscard]] bool ReaderInfo::isPukInoperative() const +{ + return mCardInfo.isPukInoperative(); +} + + +[[nodiscard]] bool ReaderInfo::isSoftwareSmartEid() const +{ + return mCardInfo.getMobileEidType() == MobileEidType::HW_KEYSTORE; +} + + +[[nodiscard]] bool ReaderInfo::wasShelved() const +{ + return mShelvedCard != CardType::NONE; +} + + +void ReaderInfo::shelveCard() +{ + mShelvedCard = mCardInfo.getCardType(); + mCardInfo.setCardType(CardType::NONE); +} + + [[nodiscard]] bool ReaderInfo::isInsertable() const { switch (mShelvedCard) @@ -62,3 +161,51 @@ } } + + +void ReaderInfo::insertCard() +{ + mCardInfo.setCardType(mShelvedCard); +} + + +void ReaderInfo::setCardInfo(const CardInfo& pCardInfo) +{ + mCardInfo = pCardInfo; +} + + +[[nodiscard]] const QString& ReaderInfo::getName() const +{ + return mName; +} + + +void ReaderInfo::setBasicReader(bool pIsBasicReader) +{ + mBasicReader = pIsBasicReader; +} + + +[[nodiscard]] bool ReaderInfo::isBasicReader() const +{ + return mBasicReader; +} + + +void ReaderInfo::setMaxApduLength(int pMaxApduLength) +{ + mMaxApduLength = pMaxApduLength; +} + + +[[nodiscard]] int ReaderInfo::getMaxApduLength() const +{ + return mMaxApduLength; +} + + +[[nodiscard]] bool ReaderInfo::insufficientApduLength() const +{ + return mMaxApduLength >= 0 && mMaxApduLength < 500; +} diff -Nru ausweisapp2-2.3.1/src/card/base/ReaderInfo.h ausweisapp2-2.4.0/src/card/base/ReaderInfo.h --- ausweisapp2-2.3.1/src/card/base/ReaderInfo.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/ReaderInfo.h 2025-10-30 10:10:48.000000000 +0000 @@ -7,7 +7,6 @@ #include "CardInfo.h" #include "ReaderConfigurationInfo.h" #include "ReaderManagerPluginInfo.h" -#include "SmartCardDefinitions.h" #include #include @@ -32,157 +31,37 @@ const CardInfo& pCardInfo = CardInfo(CardType::NONE)); [[nodiscard]] ReaderConfigurationInfo getReaderConfigurationInfo() const; + [[nodiscard]] ReaderManagerPluginType getPluginType() const; + [[nodiscard]] bool isValid() const; + void invalidate(); + [[nodiscard]] CardInfo& getCardInfo(); + [[nodiscard]] const CardInfo& getCardInfo() const; + [[nodiscard]] CardType getCardType() const; + [[nodiscard]] QString getCardTypeString() const; + + [[nodiscard]] bool hasCard() const; + [[nodiscard]] bool hasEid() const; + + [[nodiscard]] int getRetryCounter() const; + [[nodiscard]] bool isRetryCounterDetermined() const; + [[nodiscard]] bool isPinDeactivated() const; + [[nodiscard]] bool isPukInoperative() const; + [[nodiscard]] bool isSoftwareSmartEid() const; - - [[nodiscard]] ReaderManagerPluginType getPluginType() const - { - return mPluginType; - } - - - [[nodiscard]] bool isValid() const - { - return mPluginType != ReaderManagerPluginType::UNKNOWN; - } - - - void invalidate() - { - mPluginType = ReaderManagerPluginType::UNKNOWN; - mCardInfo = CardInfo(CardType::NONE); - } - - - [[nodiscard]] CardInfo& getCardInfo() - { - return mCardInfo; - } - - - [[nodiscard]] const CardInfo& getCardInfo() const - { - return mCardInfo; - } - - - [[nodiscard]] CardType getCardType() const - { - return mCardInfo.getCardType(); - } - - - [[nodiscard]] QString getCardTypeString() const - { - return mCardInfo.getCardTypeString(); - } - - - [[nodiscard]] bool hasCard() const - { - return mCardInfo.getCardType() != CardType::NONE; - } - - - [[nodiscard]] bool hasEid() const - { - return QList({CardType::EID_CARD, CardType::SMART_EID}).contains(mCardInfo.getCardType()); - } - - - [[nodiscard]] int getRetryCounter() const - { - return mCardInfo.getRetryCounter(); - } - - - [[nodiscard]] bool isRetryCounterDetermined() const - { - return mCardInfo.isRetryCounterDetermined(); - } - - - [[nodiscard]] bool isPinDeactivated() const - { - return mCardInfo.isPinDeactivated(); - } - - - [[nodiscard]] bool isPukInoperative() const - { - return mCardInfo.isPukInoperative(); - } - - - [[nodiscard]] bool isSoftwareSmartEid() const - { - return mCardInfo.getMobileEidType() == MobileEidType::HW_KEYSTORE; - } - - - [[nodiscard]] bool wasShelved() const - { - return mShelvedCard != CardType::NONE; - } - - - void shelveCard() - { - mShelvedCard = mCardInfo.getCardType(); - mCardInfo.setCardType(CardType::NONE); - } - - + [[nodiscard]] bool wasShelved() const; + void shelveCard(); [[nodiscard]] bool isInsertable() const; + void insertCard(); - void insertCard() - { - mCardInfo.setCardType(mShelvedCard); - } - - - void setCardInfo(const CardInfo& pCardInfo) - { - mCardInfo = pCardInfo; - } - - - [[nodiscard]] const QString& getName() const - { - return mName; - } - - - void setBasicReader(bool pIsBasicReader) - { - mBasicReader = pIsBasicReader; - } - - - [[nodiscard]] bool isBasicReader() const - { - return mBasicReader; - } - - - void setMaxApduLength(int pMaxApduLength) - { - mMaxApduLength = pMaxApduLength; - } - - - [[nodiscard]] int getMaxApduLength() const - { - return mMaxApduLength; - } - - - [[nodiscard]] bool insufficientApduLength() const - { - return mMaxApduLength >= 0 && mMaxApduLength < 500; - } - + void setCardInfo(const CardInfo& pCardInfo); + [[nodiscard]] const QString& getName() const; + void setBasicReader(bool pIsBasicReader); + [[nodiscard]] bool isBasicReader() const; + void setMaxApduLength(int pMaxApduLength); + [[nodiscard]] int getMaxApduLength() const; + [[nodiscard]] bool insufficientApduLength() const; }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/card/base/ReaderManager.cpp ausweisapp2-2.4.0/src/card/base/ReaderManager.cpp --- ausweisapp2-2.3.1/src/card/base/ReaderManager.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/ReaderManager.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,16 +4,20 @@ #include "ReaderManager.h" -#include - using namespace governikus; +Q_DECLARE_LOGGING_CATEGORY(card) + + QList> ReaderManager::cMainThreadInit; -Q_DECLARE_LOGGING_CATEGORY(card) +const QLoggingCategory& ReaderManager::getLoggingCategory() +{ + return card(); +} ReaderManager::ReaderManager() diff -Nru ausweisapp2-2.3.1/src/card/base/ReaderManager.h ausweisapp2-2.4.0/src/card/base/ReaderManager.h --- ausweisapp2-2.3.1/src/card/base/ReaderManager.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/ReaderManager.h 2025-10-30 10:10:48.000000000 +0000 @@ -10,6 +10,7 @@ #include "command/CreateCardConnectionCommand.h" #include "command/ExecuteCommand.h" +#include #include #include #include @@ -28,6 +29,7 @@ private: static QList> cMainThreadInit; + static const QLoggingCategory& getLoggingCategory(); mutable QMutex mMutex; QThread mThread; @@ -118,7 +120,7 @@ if (!mThread.isRunning()) { - qCWarning(card) << "Cannot call ExecuteCommand if ReaderManager-Thread is not active"; + qCWarning(getLoggingCategory()) << "Cannot call ExecuteCommand if ReaderManager-Thread is not active"; return QMetaObject::Connection(); } @@ -131,7 +133,7 @@ } else { - qCCritical(card) << "Cannot invoke ExecuteCommand command"; + qCCritical(getLoggingCategory()) << "Cannot invoke ExecuteCommand command"; command->deleteLater(); } @@ -150,7 +152,7 @@ { if (!mThread.isRunning()) { - qCWarning(card) << "Cannot call Execute if ReaderManager-Thread is not active"; + qCWarning(getLoggingCategory()) << "Cannot call Execute if ReaderManager-Thread is not active"; return; } @@ -193,7 +195,7 @@ } else { - qCCritical(card) << "Cannot invoke CreateCardConnectionCommand command"; + qCCritical(getLoggingCategory()) << "Cannot invoke CreateCardConnectionCommand command"; command->deleteLater(); } diff -Nru ausweisapp2-2.3.1/src/card/base/ReaderManagerPlugin.cpp ausweisapp2-2.4.0/src/card/base/ReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/src/card/base/ReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/ReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -8,6 +8,41 @@ using namespace governikus; +Q_DECLARE_LOGGING_CATEGORY(card) + + +void ReaderManagerPlugin::setPluginEnabled(bool pEnabled) +{ + if (mInfo.isEnabled() != pEnabled) + { + mInfo.setEnabled(pEnabled); + Q_EMIT fireStatusChanged(mInfo); + } +} + + +void ReaderManagerPlugin::setPluginAvailable(bool pAvailable) +{ + mInfo.setAvailable(pAvailable); + Q_EMIT fireStatusChanged(mInfo); +} + + +void ReaderManagerPlugin::setPluginValue(ReaderManagerPluginInfo::Key pKey, const QVariant& pValue) +{ + mInfo.setValue(pKey, pValue); +} + + +void ReaderManagerPlugin::shelve(const QPointer& pReader) +{ + if (pReader && pReader->getReaderInfo().wasShelved()) + { + pReader->shelveCard(); + } +} + + ReaderManagerPlugin::ReaderManagerPlugin(ReaderManagerPluginType pPluginType, bool pAvailable, bool pPluginEnabled) @@ -16,16 +51,28 @@ } -void ReaderManagerPlugin::shelve() const +[[nodiscard]] const ReaderManagerPluginInfo& ReaderManagerPlugin::getInfo() const { - const auto& readers = getReaders(); - for (const auto& reader : readers) - { - if (reader->getReaderInfo().wasShelved()) - { - reader->shelveCard(); - } - } + return mInfo; +} + + +void ReaderManagerPlugin::init() +{ + Q_ASSERT(QObject::thread() == QThread::currentThread()); +} + + +void ReaderManagerPlugin::shutdown() +{ + qCDebug(card).nospace() << "Shutdown ReaderManagerPluginType::" << getInfo().getPluginType(); +} + + +void ReaderManagerPlugin::reset() +{ + shutdown(); + init(); } @@ -59,3 +106,23 @@ Q_EMIT fireStatusChanged(mInfo); } } + + +void ReaderManagerPlugin::insert(const QString& pReaderName, const QVariant& pData) +{ + Q_UNUSED(pReaderName) + Q_UNUSED(pData) + + qCDebug(card).nospace() << "insert is not supported by ReaderManagerPluginType::" << getInfo().getPluginType(); +} + + +#ifndef QT_NO_DEBUG +void ReaderManagerPlugin::setPluginInfo(const ReaderManagerPluginInfo& pInfo) +{ + mInfo = pInfo; + Q_EMIT fireStatusChanged(mInfo); +} + + +#endif diff -Nru ausweisapp2-2.3.1/src/card/base/ReaderManagerPlugin.h ausweisapp2-2.4.0/src/card/base/ReaderManagerPlugin.h --- ausweisapp2-2.3.1/src/card/base/ReaderManagerPlugin.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/ReaderManagerPlugin.h 2025-10-30 10:10:48.000000000 +0000 @@ -9,6 +9,7 @@ #include "ReaderManagerPluginInfo.h" #include +#include #include @@ -25,83 +26,33 @@ ReaderManagerPluginInfo mInfo; protected: - void setPluginEnabled(bool pEnabled) - { - if (mInfo.isEnabled() != pEnabled) - { - mInfo.setEnabled(pEnabled); - Q_EMIT fireStatusChanged(mInfo); - } - } - - - void setPluginAvailable(bool pAvailable) - { - mInfo.setAvailable(pAvailable); - Q_EMIT fireStatusChanged(mInfo); - } - - - void setPluginValue(ReaderManagerPluginInfo::Key pKey, const QVariant& pValue) - { - mInfo.setValue(pKey, pValue); - } + void setPluginEnabled(bool pEnabled); + void setPluginAvailable(bool pAvailable); + void setPluginValue(ReaderManagerPluginInfo::Key pKey, const QVariant& pValue); + static void shelve(const QPointer& pReader); public: - ReaderManagerPlugin(ReaderManagerPluginType pPluginType, + explicit ReaderManagerPlugin(ReaderManagerPluginType pPluginType, bool pAvailable = false, bool pPluginEnabled = false); ~ReaderManagerPlugin() override = default; - [[nodiscard]] const ReaderManagerPluginInfo& getInfo() const - { - return mInfo; - } - - - [[nodiscard]] virtual QList getReaders() const = 0; - - - virtual void init() - { - Q_ASSERT(QObject::thread() == QThread::currentThread()); - } - - - void reset() - { - shutdown(); - init(); - } - - - virtual void shutdown() - { - } - - - virtual void insert(const QString& pReaderName, const QVariant& pData) - { - Q_UNUSED(pReaderName) - Q_UNUSED(pData) - } - - - void shelve() const; + [[nodiscard]] const ReaderManagerPluginInfo& getInfo() const; + [[nodiscard]] virtual QPointer getReader(const QString& pReaderName) const = 0; + virtual void init(); + virtual void shutdown(); + void reset(); virtual void startScan(bool pAutoConnect); virtual void stopScan(const QString& pError = QString()); void setInitialScanState(ReaderManagerPluginInfo::InitialScan pState); -#ifndef QT_NO_DEBUG - void setPluginInfo(const ReaderManagerPluginInfo& pInfo) - { - mInfo = pInfo; - Q_EMIT fireStatusChanged(mInfo); - } - + virtual void insert(const QString& pReaderName, const QVariant& pData); + virtual void shelveAll() const = 0; +#ifndef QT_NO_DEBUG + void setPluginInfo(const ReaderManagerPluginInfo& pInfo); #endif Q_SIGNALS: diff -Nru ausweisapp2-2.3.1/src/card/base/ReaderManagerPluginInfo.h ausweisapp2-2.4.0/src/card/base/ReaderManagerPluginInfo.h --- ausweisapp2-2.3.1/src/card/base/ReaderManagerPluginInfo.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/ReaderManagerPluginInfo.h 2025-10-30 10:10:48.000000000 +0000 @@ -42,7 +42,7 @@ }; public: - ReaderManagerPluginInfo(ReaderManagerPluginType pType = ReaderManagerPluginType::UNKNOWN, + explicit ReaderManagerPluginInfo(ReaderManagerPluginType pType = ReaderManagerPluginType::UNKNOWN, bool pEnabled = false, bool pAvailable = false); diff -Nru ausweisapp2-2.3.1/src/card/base/ReaderManagerWorker.cpp ausweisapp2-2.4.0/src/card/base/ReaderManagerWorker.cpp --- ausweisapp2-2.3.1/src/card/base/ReaderManagerWorker.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/ReaderManagerWorker.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -11,12 +11,13 @@ #include #include -#include Q_DECLARE_LOGGING_CATEGORY(card) + using namespace governikus; + INIT_FUNCTION([] { qRegisterMetaType>("QSharedPointer"); }) @@ -162,7 +163,7 @@ for (const auto& plugin : std::as_const(mPlugins)) { callOnPlugin(plugin->getInfo().getPluginType(), [](const ReaderManagerPlugin* pPlugin){ - pPlugin->shelve(); + pPlugin->shelveAll(); }, "Shelve"); } } @@ -195,29 +196,12 @@ } -QList ReaderManagerWorker::getReaderInfos() const -{ - Q_ASSERT(QObject::thread() == QThread::currentThread()); - - QList list; - for (const auto& plugin : std::as_const(mPlugins)) - { - const auto& readerList = plugin->getReaders(); - for (const Reader* const reader : readerList) - { - list += reader->getReaderInfo(); - } - } - return list; -} - - void ReaderManagerWorker::updateReaderInfo(const QString& pReaderName) const { Q_ASSERT(QObject::thread() == QThread::currentThread()); - Reader* reader = getReader(pReaderName); - if (reader == nullptr) + const auto& reader = getReader(pReaderName); + if (!reader) { qCWarning(card) << "Requested reader does not exist:" << pReaderName; return; @@ -226,19 +210,16 @@ } -Reader* ReaderManagerWorker::getReader(const QString& pReaderName) const +QPointer ReaderManagerWorker::getReader(const QString& pReaderName) const { Q_ASSERT(QObject::thread() == QThread::currentThread()); for (auto& plugin : std::as_const(mPlugins)) { - const auto& readerList = plugin->getReaders(); - for (Reader* reader : readerList) + const auto& reader = plugin->getReader(pReaderName); + if (reader) { - if (reader->getName() == pReaderName) - { - return reader; - } + return reader; } } diff -Nru ausweisapp2-2.3.1/src/card/base/ReaderManagerWorker.h ausweisapp2-2.4.0/src/card/base/ReaderManagerWorker.h --- ausweisapp2-2.3.1/src/card/base/ReaderManagerWorker.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/ReaderManagerWorker.h 2025-10-30 10:10:48.000000000 +0000 @@ -10,6 +10,7 @@ #include "ReaderManagerPluginInfo.h" #include +#include namespace governikus @@ -26,7 +27,7 @@ void registerPlugins(); [[nodiscard]] static bool isPlugin(const QJsonObject& pJson); void registerPlugin(ReaderManagerPlugin* pPlugin); - [[nodiscard]] Reader* getReader(const QString& pReaderName) const; + [[nodiscard]] QPointer getReader(const QString& pReaderName) const; public: ReaderManagerWorker(); @@ -40,7 +41,6 @@ Q_INVOKABLE void startScan(ReaderManagerPluginType pType, bool pAutoConnect); Q_INVOKABLE void stopScan(ReaderManagerPluginType pType, const QString& pError); - [[nodiscard]] Q_INVOKABLE QList getReaderInfos() const; Q_INVOKABLE void updateReaderInfo(const QString& pReaderName) const; void createCardConnectionWorker(const QString& pReaderName, const std::function(const QSharedPointer&)>& pInitWorker); diff -Nru ausweisapp2-2.3.1/src/card/base/apdu/CommandApdu.cpp ausweisapp2-2.4.0/src/card/base/apdu/CommandApdu.cpp --- ausweisapp2-2.3.1/src/card/base/apdu/CommandApdu.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/apdu/CommandApdu.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -107,14 +107,14 @@ CommandApdu::CommandApdu(const QByteArray& pHeader, const QByteArray& pData, int pLe) : - mCla(static_cast(pHeader.size() > 0 ? pHeader.at(0) : 0)), + mCla(static_cast(pHeader.isEmpty() ? 0 : pHeader.at(0))), mIns(pHeader.size() > 1 ? static_cast(pHeader.at(1)) : 0), mP1(pHeader.size() > 2 ? static_cast(pHeader.at(2)) : 0), mP2(pHeader.size() > 3 ? static_cast(pHeader.at(3)) : 0), mData(pData), mLe(pLe) { - if (pHeader.size() > 0 && pHeader.size() != 4) + if (!pHeader.isEmpty() && pHeader.size() != 4) { qCCritical(card) << "Wrong command header size!"; } @@ -277,7 +277,7 @@ cmd += '\0'; } - if (mData.size() > 0) + if (!mData.isEmpty()) { cmd += generateLengthField(static_cast(mData.size())); cmd += mData; diff -Nru ausweisapp2-2.3.1/src/card/base/apdu/CommandApdu.h ausweisapp2-2.4.0/src/card/base/apdu/CommandApdu.h --- ausweisapp2-2.3.1/src/card/base/apdu/CommandApdu.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/apdu/CommandApdu.h 2025-10-30 10:10:48.000000000 +0000 @@ -29,7 +29,6 @@ RESET_RETRY_COUNTER = 0x2C, SELECT = 0xA4, READ_BINARY = 0xB0, - GET_RESPONSE = 0xC0, UPDATE_BINARY = 0xD6 ) diff -Nru ausweisapp2-2.3.1/src/card/base/apdu/ResponseApdu.h ausweisapp2-2.4.0/src/card/base/apdu/ResponseApdu.h --- ausweisapp2-2.3.1/src/card/base/apdu/ResponseApdu.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/apdu/ResponseApdu.h 2025-10-30 10:10:48.000000000 +0000 @@ -44,7 +44,7 @@ FILE_NOT_FOUND = 0x6A82, RECORD_NOT_FOUND = 0x6A83, INVALID_PARAMETER = 0x6A86, - LC_INCONSISTANT = 0x6A87, + LC_INCONSISTENT = 0x6A87, REFERENCED_DATA_NOT_FOUND = 0x6A88, ILLEGAL_OFFSET = 0x6B00, UNSUPPORTED_CLA = 0x6E00, diff -Nru ausweisapp2-2.3.1/src/card/base/asn1/ASN1TemplateUtil.cpp ausweisapp2-2.4.0/src/card/base/asn1/ASN1TemplateUtil.cpp --- ausweisapp2-2.3.1/src/card/base/asn1/ASN1TemplateUtil.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/asn1/ASN1TemplateUtil.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,6 +4,15 @@ #include "ASN1TemplateUtil.h" + +Q_DECLARE_LOGGING_CATEGORY(card) + + +const QLoggingCategory& governikus::getLoggingCategory() +{ + return card(); +} + QByteArray governikus::getOpenSslError() { QByteArrayList list; diff -Nru ausweisapp2-2.3.1/src/card/base/asn1/ASN1TemplateUtil.h ausweisapp2-2.4.0/src/card/base/asn1/ASN1TemplateUtil.h --- ausweisapp2-2.3.1/src/card/base/asn1/ASN1TemplateUtil.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/asn1/ASN1TemplateUtil.h 2025-10-30 10:10:48.000000000 +0000 @@ -13,12 +13,10 @@ #include -Q_DECLARE_LOGGING_CATEGORY(card) - - namespace governikus { +const QLoggingCategory& getLoggingCategory(); QByteArray getOpenSslError(); /*! @@ -76,7 +74,7 @@ }); if (length < 0) { - qCWarning(card) << "Cannot encode ASN.1 object:" << getOpenSslError(); + qCWarning(governikus::getLoggingCategory()) << "Cannot encode ASN.1 object:" << getOpenSslError(); return QByteArray(); } @@ -117,7 +115,7 @@ const auto* dataPointer = reinterpret_cast(pData.constData()); if (!decodeAsn1Object(&object, &dataPointer, static_cast(pData.length())) && pLogging) { - qCWarning(card) << "Cannot decode ASN.1 object:" << getOpenSslError(); + qCWarning(governikus::getLoggingCategory()) << "Cannot decode ASN.1 object:" << getOpenSslError(); } static auto deleter = [](T* pTypeObject) diff -Nru ausweisapp2-2.3.1/src/card/base/asn1/CVCertificate.cpp ausweisapp2-2.4.0/src/card/base/asn1/CVCertificate.cpp --- ausweisapp2-2.3.1/src/card/base/asn1/CVCertificate.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/asn1/CVCertificate.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -113,12 +113,13 @@ QDebug operator <<(QDebug pDbg, const governikus::CVCertificate& pCvc) { QDebugStateSaver saver(pDbg); - pDbg.nospace() << "CVC(type=" << pCvc.getBody().getCHAT().getAccessRole() - << ", car=" << pCvc.getBody().getCertificationAuthorityReference() - << ", chr=" << pCvc.getBody().getCertificateHolderReference() - << ", valid=[" << pCvc.getBody().getCertificateEffectiveDate().toString(Qt::ISODate) - << "," << pCvc.getBody().getCertificateExpirationDate().toString(Qt::ISODate) << "]=" - << pCvc.isValidOn(QDateTime::currentDateTime()) << ")"; + pDbg.nospace().noquote() << pCvc.getBody().getCHAT().getAccessRole() << "(" + << pCvc.getBody().getCertificateHolderReference() + << ", authority=" << pCvc.getBody().getCertificationAuthorityReference() + << ", " << pCvc.getBody().getCertificateEffectiveDate().toString(Qt::ISODate) + << " - " << pCvc.getBody().getCertificateExpirationDate().toString(Qt::ISODate) + << ", " << (pCvc.isValidOn(QDateTime::currentDateTime()) ? "valid" : "invalid") + << ")"; return pDbg; } @@ -138,21 +139,20 @@ } -QDebug operator<<(QDebug pDbg, const QSharedPointer& pCvc) +QDebug operator<<(QDebug pDbg, const QList>& pCvcs) { - pDbg << QSharedPointer(pCvc); - return pDbg; -} - - -QDebug operator<<(QDebug pDbg, const QList>& pCvcs) -{ - QDebugStateSaver saver(pDbg); - pDbg.nospace() << "QList("; + QByteArrayList names; for (const auto& cvc : pCvcs) { - pDbg.nospace() << cvc << ","; + QByteArray holder = cvc->getBody().getCertificateHolderReference(); + if (cvc->isIssuedBy(*cvc)) + { + holder.prepend("(Self Signed) "); + } + names << holder; } - pDbg.nospace() << ")"; + + QDebugStateSaver saver(pDbg); + pDbg.noquote().nospace() << "[" << names.join(", ") << "]"; return pDbg; } diff -Nru ausweisapp2-2.3.1/src/card/base/asn1/CVCertificate.h ausweisapp2-2.4.0/src/card/base/asn1/CVCertificate.h --- ausweisapp2-2.3.1/src/card/base/asn1/CVCertificate.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/asn1/CVCertificate.h 2025-10-30 10:10:48.000000000 +0000 @@ -83,5 +83,4 @@ QDebug operator<<(QDebug pDbg, const governikus::CVCertificate& pCvc); QDebug operator<<(QDebug pDbg, const QSharedPointer& pCvc); -QDebug operator<<(QDebug pDbg, const QSharedPointer& pCvc); -QDebug operator<<(QDebug pDbg, const QList>& pCvcs); +QDebug operator<<(QDebug pDbg, const QList>& pCvcs); diff -Nru ausweisapp2-2.3.1/src/card/base/asn1/CVCertificateChainBuilder.cpp ausweisapp2-2.4.0/src/card/base/asn1/CVCertificateChainBuilder.cpp --- ausweisapp2-2.3.1/src/card/base/asn1/CVCertificateChainBuilder.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/asn1/CVCertificateChainBuilder.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,6 +6,7 @@ #include + using namespace governikus; @@ -32,11 +33,6 @@ { removeInvalidChains(); - for (const auto& cvc : pCvcPool) - { - qCDebug(card) << "CVC in pool" << cvc; - } - if (getChains().isEmpty()) { qCWarning(card) << "No valid chains could be built"; @@ -53,14 +49,9 @@ void CVCertificateChainBuilder::removeInvalidChains() { - auto chainIter = getChainIterator(); - while (chainIter.hasNext()) - { - if (!CVCertificateChain(chainIter.next(), mProductive).isValid()) - { - chainIter.remove(); - } - } + removeFromChains([this](const QList>& pChain){ + return !CVCertificateChain(pChain, mProductive).isValid(); + }); } diff -Nru ausweisapp2-2.3.1/src/card/base/asn1/ChainBuilder.h ausweisapp2-2.4.0/src/card/base/asn1/ChainBuilder.h --- ausweisapp2-2.3.1/src/card/base/asn1/ChainBuilder.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/asn1/ChainBuilder.h 2025-10-30 10:10:48.000000000 +0000 @@ -62,9 +62,9 @@ } protected: - QMutableListIterator> getChainIterator() + void removeFromChains(const std::function& pChain)>& pFunc) { - return QMutableListIterator>(mChains); + erase_if(mChains, pFunc); } public: diff -Nru ausweisapp2-2.3.1/src/card/base/asn1/EFCardSecurity.h ausweisapp2-2.4.0/src/card/base/asn1/EFCardSecurity.h --- ausweisapp2-2.3.1/src/card/base/asn1/EFCardSecurity.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/asn1/EFCardSecurity.h 2025-10-30 10:10:48.000000000 +0000 @@ -67,7 +67,7 @@ * sid SignerIdentifier, * digestAlgorithm DigestAlgorithmIdentifier, * signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL, - * signatureAlgoritm SignatureAlgorithmIdentifier, + * signatureAlgorithm SignatureAlgorithmIdentifier, * signature SignatureValue, * unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL * } diff -Nru ausweisapp2-2.3.1/src/card/base/command/UpdateRetryCounterCommand.cpp ausweisapp2-2.4.0/src/card/base/command/UpdateRetryCounterCommand.cpp --- ausweisapp2-2.3.1/src/card/base/command/UpdateRetryCounterCommand.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/command/UpdateRetryCounterCommand.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -7,8 +7,9 @@ using namespace governikus; -UpdateRetryCounterCommand::UpdateRetryCounterCommand(QSharedPointer pCardConnectionWorker) +UpdateRetryCounterCommand::UpdateRetryCounterCommand(QSharedPointer pCardConnectionWorker, const QString& pSlotHandle) : BaseCardCommand(pCardConnectionWorker) + , mSlotHandle(pSlotHandle) { } @@ -17,3 +18,9 @@ { setReturnCode(getCardConnectionWorker()->updateRetryCounter()); } + + +const QString& UpdateRetryCounterCommand::getSlotHandle() const +{ + return mSlotHandle; +} diff -Nru ausweisapp2-2.3.1/src/card/base/command/UpdateRetryCounterCommand.h ausweisapp2-2.4.0/src/card/base/command/UpdateRetryCounterCommand.h --- ausweisapp2-2.3.1/src/card/base/command/UpdateRetryCounterCommand.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/command/UpdateRetryCounterCommand.h 2025-10-30 10:10:48.000000000 +0000 @@ -19,13 +19,17 @@ Q_OBJECT friend class ::test_UpdateRetryCounterCommand; + private: + const QString mSlotHandle; + protected: void internalExecute() override; ~UpdateRetryCounterCommand() override = default; public: - explicit UpdateRetryCounterCommand(QSharedPointer pCardConnectionWorker); + explicit UpdateRetryCounterCommand(QSharedPointer pCardConnectionWorker, const QString& pSlotHandle); + [[nodiscard]] const QString& getSlotHandle() const; }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/card/base/pace/ec/EcUtil.cpp ausweisapp2-2.4.0/src/card/base/pace/ec/EcUtil.cpp --- ausweisapp2-2.3.1/src/card/base/pace/ec/EcUtil.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/pace/ec/EcUtil.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -149,8 +149,13 @@ return nullptr; } - if (OSSL_PARAM* params = nullptr; - pFunc(bld) && (params = OSSL_PARAM_BLD_to_param(bld)) != nullptr) + if (!pFunc(bld)) + { + qCCritical(card) << "Cannot initialize parameter builder"; + return nullptr; + } + + if (OSSL_PARAM* params = OSSL_PARAM_BLD_to_param(bld); params != nullptr) { static auto deleter = [](OSSL_PARAM* pParam) { diff -Nru ausweisapp2-2.3.1/src/card/base/pinpad/EstablishPaceChannel.h ausweisapp2-2.4.0/src/card/base/pinpad/EstablishPaceChannel.h --- ausweisapp2-2.3.1/src/card/base/pinpad/EstablishPaceChannel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/pinpad/EstablishPaceChannel.h 2025-10-30 10:10:48.000000000 +0000 @@ -46,7 +46,7 @@ QByteArray mPassword; public: - EstablishPaceChannel( + explicit EstablishPaceChannel( PacePasswordId pPasswordId = PacePasswordId::UNKNOWN, const QByteArray& pChat = QByteArray(), const QByteArray& pCertificateDescription = QByteArray()); diff -Nru ausweisapp2-2.3.1/src/card/base/pinpad/EstablishPaceChannelOutput.cpp ausweisapp2-2.4.0/src/card/base/pinpad/EstablishPaceChannelOutput.cpp --- ausweisapp2-2.3.1/src/card/base/pinpad/EstablishPaceChannelOutput.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/pinpad/EstablishPaceChannelOutput.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -94,6 +94,9 @@ return CardReturnCode::INVALID_PASSWORD; } break; + + default: + break; } return CardReturnCode::UNKNOWN; @@ -247,12 +250,6 @@ mCarCurr.clear(); mCarPrev.clear(); - if (pOutput.isEmpty()) - { - qCDebug(card) << "No more data available"; - return true; - } - if (pOutput.size() < 4) { qCCritical(card) << "OutputData too short"; diff -Nru ausweisapp2-2.3.1/src/card/base/pinpad/PinModifyOutput.cpp ausweisapp2-2.4.0/src/card/base/pinpad/PinModifyOutput.cpp --- ausweisapp2-2.3.1/src/card/base/pinpad/PinModifyOutput.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/pinpad/PinModifyOutput.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -/** - * Copyright (c) 2014-2025 Governikus GmbH & Co. KG, Germany - */ - -#include "PinModifyOutput.h" - -#include -#include - - -using namespace governikus; - - -Q_DECLARE_LOGGING_CATEGORY(card) - - -PinModifyOutput::PinModifyOutput() - : mResponseApdu() -{ -} - - -PinModifyOutput::PinModifyOutput(const ResponseApdu& pResponseApdu) - : mResponseApdu(pResponseApdu) -{ -} - - -QByteArray PinModifyOutput::toCcid() const -{ - return mResponseApdu; -} - - -const ResponseApdu& PinModifyOutput::getResponseApdu() const -{ - return mResponseApdu; -} diff -Nru ausweisapp2-2.3.1/src/card/base/pinpad/PinModifyOutput.h ausweisapp2-2.4.0/src/card/base/pinpad/PinModifyOutput.h --- ausweisapp2-2.3.1/src/card/base/pinpad/PinModifyOutput.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/base/pinpad/PinModifyOutput.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -/** - * Copyright (c) 2014-2025 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "apdu/ResponseApdu.h" - -#include - - -namespace governikus -{ - -class PinModifyOutput -{ - private: - ResponseApdu mResponseApdu; - - public: - PinModifyOutput(); - explicit PinModifyOutput(const ResponseApdu& pResponseApdu); - - [[nodiscard]] const ResponseApdu& getResponseApdu() const; - - /** - * Defined in TR-03119 Section D.3 and DWG_Smart-Card_CCID_Rev110 - * Section 6.2.1 - */ - [[nodiscard]] QByteArray toCcid() const; - -}; - -} // namespace governikus diff -Nru ausweisapp2-2.3.1/src/card/drivers/DeviceListener.cpp ausweisapp2-2.4.0/src/card/drivers/DeviceListener.cpp --- ausweisapp2-2.3.1/src/card/drivers/DeviceListener.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/drivers/DeviceListener.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -11,7 +11,6 @@ #include #include #include -#include using namespace governikus; diff -Nru ausweisapp2-2.3.1/src/card/nfc/NfcReader.cpp ausweisapp2-2.4.0/src/card/nfc/NfcReader.cpp --- ausweisapp2-2.3.1/src/card/nfc/NfcReader.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/nfc/NfcReader.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -51,22 +51,14 @@ mCard.reset(new NfcCard(pTarget)); connect(mCard.data(), &NfcCard::fireSetProgressMessage, this, &NfcReader::setProgressMessage); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) - #define GOV_UNSUPPORTED_TARGET_ERROR UnsupportedTargetError -#elif defined(GOVERNIKUS_QT) && (QT_VERSION >= QT_VERSION_CHECK(6, 6, 3)) - #define GOV_UNSUPPORTED_TARGET_ERROR SecurityViolation -#endif -#ifdef GOV_UNSUPPORTED_TARGET_ERROR connect(mCard.data(), &NfcCard::fireTargetError, this, [this](QNearFieldTarget::Error pError) { - if (pError == QNearFieldTarget::GOV_UNSUPPORTED_TARGET_ERROR) + if (pError == QNearFieldTarget::UnsupportedTargetError) { setInfoCardInfo(CardInfo(CardType::UNKNOWN)); qCInfo(card_nfc) << "Card inserted:" << getReaderInfo().getCardInfo(); Q_EMIT fireCardInserted(getReaderInfo()); } }); - #undef GOV_UNSUPPORTED_TARGET_ERROR -#endif fetchCardInfo(); if (!getCard()) diff -Nru ausweisapp2-2.3.1/src/card/nfc/NfcReader.h ausweisapp2-2.4.0/src/card/nfc/NfcReader.h --- ausweisapp2-2.3.1/src/card/nfc/NfcReader.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/nfc/NfcReader.h 2025-10-30 10:10:48.000000000 +0000 @@ -39,7 +39,7 @@ [[nodiscard]] Card* getCard() const override; void connectReader() override; - void disconnectReader(const QString& pError = QString()) override; + void disconnectReader(const QString& pError) override; }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/card/nfc/NfcReaderManagerPlugin.cpp ausweisapp2-2.4.0/src/card/nfc/NfcReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/src/card/nfc/NfcReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/nfc/NfcReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -22,7 +22,7 @@ void NfcReaderManagerPlugin::onNfcAdapterStateChanged(bool pEnabled) { - if (getInfo().isEnabled() == pEnabled || !mNfcReader) + if (getInfo().isEnabled() == pEnabled || !mReader) { return; } @@ -31,7 +31,7 @@ setPluginEnabled(pEnabled); if (pEnabled) { - Q_EMIT fireReaderAdded(mNfcReader->getReaderInfo()); + Q_EMIT fireReaderAdded(mReader->getReaderInfo()); #if defined(Q_OS_ANDROID) if (QNativeInterface::QAndroidApplication::isActivityContext()) { @@ -44,7 +44,7 @@ } else { - Q_EMIT fireReaderRemoved(mNfcReader->getReaderInfo()); + Q_EMIT fireReaderRemoved(mReader->getReaderInfo()); } } @@ -76,7 +76,7 @@ if (auto* plugin = NfcReaderManagerPlugin::instance.loadRelaxed()) { QMetaObject::invokeMethod(plugin, [plugin, pEnabled] { - if (plugin->mNfcReader) + if (plugin->mReader) { setReaderMode(pEnabled); } @@ -107,7 +107,7 @@ : ReaderManagerPlugin(ReaderManagerPluginType::NFC, QNearFieldManager().isSupported(QNearFieldTarget::TagTypeSpecificAccess) ) - , mNfcReader(nullptr) + , mReader(nullptr) { instance = this; } @@ -119,14 +119,14 @@ } -QList NfcReaderManagerPlugin::getReaders() const +QPointer NfcReaderManagerPlugin::getReader(const QString& pReaderName) const { - if (getInfo().isEnabled() && mNfcReader) + if (getInfo().isEnabled() && mReader && mReader->getName() == pReaderName) { - return QList({mNfcReader.data()}); + return mReader.data(); } - return QList(); + return nullptr; } @@ -134,41 +134,41 @@ { ReaderManagerPlugin::init(); - if (!getInfo().isAvailable() || mNfcReader) + if (!getInfo().isAvailable() || mReader) { return; } - mNfcReader.reset(new NfcReader()); - connect(mNfcReader.data(), &NfcReader::fireCardInserted, this, &NfcReaderManagerPlugin::fireCardInserted); - connect(mNfcReader.data(), &NfcReader::fireCardRemoved, this, &NfcReaderManagerPlugin::fireCardRemoved); - connect(mNfcReader.data(), &NfcReader::fireCardInfoChanged, this, &NfcReaderManagerPlugin::fireCardInfoChanged); - connect(mNfcReader.data(), &NfcReader::fireReaderPropertiesUpdated, this, &NfcReaderManagerPlugin::fireReaderPropertiesUpdated); - connect(mNfcReader.data(), &NfcReader::fireNfcAdapterStateChanged, this, &NfcReaderManagerPlugin::onNfcAdapterStateChanged); - connect(mNfcReader.data(), &NfcReader::fireReaderDisconnected, this, &NfcReaderManagerPlugin::onReaderDisconnected); - qCDebug(card_nfc) << "Add reader" << mNfcReader->getName(); + mReader.reset(new NfcReader()); + connect(mReader.data(), &NfcReader::fireCardInserted, this, &NfcReaderManagerPlugin::fireCardInserted); + connect(mReader.data(), &NfcReader::fireCardRemoved, this, &NfcReaderManagerPlugin::fireCardRemoved); + connect(mReader.data(), &NfcReader::fireCardInfoChanged, this, &NfcReaderManagerPlugin::fireCardInfoChanged); + connect(mReader.data(), &NfcReader::fireReaderPropertiesUpdated, this, &NfcReaderManagerPlugin::fireReaderPropertiesUpdated); + connect(mReader.data(), &NfcReader::fireNfcAdapterStateChanged, this, &NfcReaderManagerPlugin::onNfcAdapterStateChanged); + connect(mReader.data(), &NfcReader::fireReaderDisconnected, this, &NfcReaderManagerPlugin::onReaderDisconnected); + qCDebug(card_nfc) << "Add reader" << mReader->getName(); setReaderMode(true); - onNfcAdapterStateChanged(mNfcReader->isEnabled()); + onNfcAdapterStateChanged(mReader->isEnabled()); } void NfcReaderManagerPlugin::shutdown() { - if (mNfcReader) + if (mReader) { onNfcAdapterStateChanged(false); setReaderMode(false); - mNfcReader.reset(); + mReader.reset(); } } void NfcReaderManagerPlugin::startScan(bool pAutoConnect) { - if (mNfcReader) + if (mReader) { - mNfcReader->connectReader(); + mReader->connectReader(); ReaderManagerPlugin::startScan(pAutoConnect); } } @@ -176,9 +176,18 @@ void NfcReaderManagerPlugin::stopScan(const QString& pError) { - if (mNfcReader) + if (mReader) { - mNfcReader->disconnectReader(pError); + mReader->disconnectReader(pError); ReaderManagerPlugin::stopScan(pError); } } + + +void NfcReaderManagerPlugin::shelveAll() const +{ + if (getInfo().isEnabled() && mReader) + { + shelve(mReader.data()); + } +} diff -Nru ausweisapp2-2.3.1/src/card/nfc/NfcReaderManagerPlugin.h ausweisapp2-2.4.0/src/card/nfc/NfcReaderManagerPlugin.h --- ausweisapp2-2.3.1/src/card/nfc/NfcReaderManagerPlugin.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/nfc/NfcReaderManagerPlugin.h 2025-10-30 10:10:48.000000000 +0000 @@ -24,7 +24,7 @@ private: static QAtomicPointer instance; - QScopedPointer mNfcReader; + QScopedPointer mReader; private Q_SLOTS: void onNfcAdapterStateChanged(bool pEnabled); @@ -37,13 +37,15 @@ NfcReaderManagerPlugin(); ~NfcReaderManagerPlugin() override; - [[nodiscard]] QList getReaders() const override; + [[nodiscard]] QPointer getReader(const QString& pReaderName) const override; void init() override; void shutdown() override; void startScan(bool pAutoConnect) override; - void stopScan(const QString& pError = QString()) override; + void stopScan(const QString& pError) override; + + void shelveAll() const override; }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/card/pcsc/PcscCard.cpp ausweisapp2-2.4.0/src/card/pcsc/PcscCard.cpp --- ausweisapp2-2.3.1/src/card/pcsc/PcscCard.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/pcsc/PcscCard.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -171,35 +171,6 @@ return {CardReturnCode::COMMAND_FAILED}; } - ResponseApdu tempResponse(data.mResponse); - - if (tempResponse.getSW1() == SW1::WRONG_LE_FIELD) - { - qCDebug(card_pcsc) << "got SW1 == 0x6C, retransmitting with new Le:" << tempResponse.getSW2(); - CommandApdu retransmitCommand(pCmd.getHeaderBytes(), pCmd.getData(), tempResponse.getSW2()); - data = transmit(QByteArray(retransmitCommand)); - if (data.mReturnCode != pcsc::Scard_S_Success) - { - return {CardReturnCode::COMMAND_FAILED}; - } - } - - while (tempResponse.getSW1() == SW1::MORE_DATA_AVAILABLE) - { - qCDebug(card_pcsc) << "got SW1 == 0x61, getting response with Le:" << tempResponse.getSW2(); - CommandApdu getResponseCommand(Ins::GET_RESPONSE, 0, 0, QByteArray(), tempResponse.getSW2()); - CardResult tmpData = transmit(QByteArray(getResponseCommand)); - if (data.mReturnCode != pcsc::Scard_S_Success) - { - return {CardReturnCode::COMMAND_FAILED}; - } - - tempResponse = ResponseApdu(tmpData.mResponse); - // cut off sw1 and sw2 - data.mResponse.resize(data.mResponse.size() - 2); - data.mResponse += tmpData.mResponse; - } - return {CardReturnCode::OK, ResponseApdu(data.mResponse)}; } diff -Nru ausweisapp2-2.3.1/src/card/pcsc/PcscReader.cpp ausweisapp2-2.4.0/src/card/pcsc/PcscReader.cpp --- ausweisapp2-2.3.1/src/card/pcsc/PcscReader.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/pcsc/PcscReader.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -290,12 +290,12 @@ QByteArray buffer(1024, '\0'); const std::array inBuffer({0, 0}); - PCSC_INT clen = 0; + PCSC_INT length = 0; returnCode = SCardControl(cardHandle, CM_IOCTL_GET_FEATURE_REQUEST, inBuffer.data(), static_cast(inBuffer.size()), buffer.data(), static_cast(buffer.size()), - &clen); - buffer.resize(static_cast(clen)); + &length); + buffer.resize(static_cast(length)); qCDebug(card_pcsc) << "SCardControl for" << readerName << ':' << pcsc::toString(returnCode); if (returnCode != pcsc::Scard_S_Success) diff -Nru ausweisapp2-2.3.1/src/card/pcsc/PcscReaderManagerPlugin.cpp ausweisapp2-2.4.0/src/card/pcsc/PcscReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/src/card/pcsc/PcscReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/pcsc/PcscReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,8 +4,6 @@ #include "PcscReaderManagerPlugin.h" -#include "PcscReader.h" - #include @@ -42,9 +40,9 @@ } -QList PcscReaderManagerPlugin::getReaders() const +QPointer PcscReaderManagerPlugin::getReader(const QString& pReaderName) const { - return mReaders.values(); + return mReaders.value(pReaderName).data(); } @@ -124,15 +122,9 @@ } QStringList readersToRemove(mReaders.keys()); - for (QMutableListIterator it(readersToAdd); it.hasNext();) - { - QString readerName = it.next(); - if (readersToRemove.contains(readerName)) - { - readersToRemove.removeOne(readerName); - it.remove(); - } - } + erase_if(readersToAdd, [&readersToRemove](const auto& pReader){ + return readersToRemove.removeOne(pReader); + }); removeReaders(readersToRemove); addReaders(readersToAdd); @@ -156,19 +148,18 @@ { for (const auto& readerName : pReaderNames) { - auto pcscReader = std::make_unique(readerName); - if (pcscReader->init() != pcsc::Scard_S_Success) + const auto& reader = QSharedPointer::create(readerName); + if (reader->init() != pcsc::Scard_S_Success) { qCDebug(card_pcsc) << "Initialization of" << readerName << "failed"; continue; } - Reader* reader = pcscReader.release(); mReaders.insert(readerName, reader); - connect(reader, &Reader::fireCardInserted, this, &PcscReaderManagerPlugin::fireCardInserted); - connect(reader, &Reader::fireCardRemoved, this, &PcscReaderManagerPlugin::fireCardRemoved); - connect(reader, &Reader::fireCardInfoChanged, this, &PcscReaderManagerPlugin::fireCardInfoChanged); + connect(reader.data(), &Reader::fireCardInserted, this, &PcscReaderManagerPlugin::fireCardInserted); + connect(reader.data(), &Reader::fireCardRemoved, this, &PcscReaderManagerPlugin::fireCardRemoved); + connect(reader.data(), &Reader::fireCardInfoChanged, this, &PcscReaderManagerPlugin::fireCardInfoChanged); qCDebug(card_pcsc) << "fireReaderAdded:" << readerName << "(" << mReaders.size() << "reader in total )"; Q_EMIT fireReaderAdded(reader->getReaderInfo()); @@ -184,10 +175,7 @@ Q_ASSERT(false); } - auto* reader = mReaders.take(pReaderName); - auto info = reader->getReaderInfo(); - delete reader; - + const auto info = mReaders.take(pReaderName)->getReaderInfo(); Q_EMIT fireReaderRemoved(info); } @@ -233,3 +221,12 @@ return returnCode; } + + +void PcscReaderManagerPlugin::shelveAll() const +{ + for (const auto& reader : mReaders) + { + shelve(reader.data()); + } +} diff -Nru ausweisapp2-2.3.1/src/card/pcsc/PcscReaderManagerPlugin.h ausweisapp2-2.4.0/src/card/pcsc/PcscReaderManagerPlugin.h --- ausweisapp2-2.3.1/src/card/pcsc/PcscReaderManagerPlugin.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/pcsc/PcscReaderManagerPlugin.h 2025-10-30 10:10:48.000000000 +0000 @@ -4,11 +4,12 @@ #pragma once +#include "PcscReader.h" #include "PcscUtils.h" -#include "Reader.h" #include "ReaderManagerPlugin.h" #include +#include #include #include @@ -30,7 +31,7 @@ private: SCARDCONTEXT mContextHandle; QTimer mTimer; - QMap mReaders; + QMap> mReaders; private: PCSC_RETURNCODE readReaderNames(QStringList& pReaderNames) const; @@ -44,10 +45,12 @@ PcscReaderManagerPlugin(); ~PcscReaderManagerPlugin() override; - [[nodiscard]] QList getReaders() const override; + [[nodiscard]] QPointer getReader(const QString& pReaderName) const override; void startScan(bool pAutoConnect) override; void stopScan(const QString& pError = QString()) override; + + void shelveAll() const override; }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/card/simulator/SimulatorFileSystem.cpp ausweisapp2-2.4.0/src/card/simulator/SimulatorFileSystem.cpp --- ausweisapp2-2.3.1/src/card/simulator/SimulatorFileSystem.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/simulator/SimulatorFileSystem.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -157,16 +157,6 @@ { const auto& keyId = pKey[QLatin1String("id")].toInt(0); auto privateKey = pKey[QLatin1String("content")].toString(); - if (privateKey.isNull()) // Migration: With 2.2.0 the name and format of the private key was changed - { - if (const auto& rawKey = pKey[QLatin1String("private")].toString(); !rawKey.isNull()) - { - privateKey = QStringView(u"308188020100301406072a8648ce3d020106092b2403030208010107046d306b02010104" - "20%1a1440342000483dd43a94436965ab3048c66d2932e200055b5d3448cdaebd5f3814d" - "1ba6fc213dccd45fd48e18303d6625f5831e9a9efb6747481209d440588c046f2d188b5b").arg(rawKey); - qCWarning(card_simulator) << "'private' is deprecated and was replaced by 'content'"; - } - } if (keyId == 0 || privateKey.isNull()) { qCWarning(card_simulator) << "Skipping key entry. Expected JSON object with 'id' and 'content', got" << pKey; diff -Nru ausweisapp2-2.3.1/src/card/simulator/SimulatorReaderManagerPlugin.cpp ausweisapp2-2.4.0/src/card/simulator/SimulatorReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/src/card/simulator/SimulatorReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/simulator/SimulatorReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -16,7 +16,7 @@ SimulatorReaderManagerPlugin::SimulatorReaderManagerPlugin() : ReaderManagerPlugin(ReaderManagerPluginType::SIMULATOR, true) - , mSimulatorReader() + , mReader() { connect(&Env::getSingleton()->getSimulatorSettings(), &SimulatorSettings::fireEnabledChanged, this, &SimulatorReaderManagerPlugin::onSettingsChanged); connect(Env::getSingleton(), &VolatileSettings::fireUsedAsSdkChanged, this, &SimulatorReaderManagerPlugin::onSettingsChanged); @@ -31,13 +31,14 @@ } -QList SimulatorReaderManagerPlugin::getReaders() const +QPointer SimulatorReaderManagerPlugin::getReader(const QString& pReaderName) const { - if (getInfo().isEnabled() && mSimulatorReader) + if (getInfo().isEnabled() && mReader && mReader->getName() == pReaderName) { - return {mSimulatorReader.data()}; + return mReader.data(); } - return {}; + + return nullptr; } @@ -45,15 +46,15 @@ { if (getInfo().isEnabled()) { - mSimulatorReader.reset(new SimulatorReader()); + mReader.reset(new SimulatorReader()); - connect(mSimulatorReader.data(), &SimulatorReader::fireReaderPropertiesUpdated, this, &SimulatorReaderManagerPlugin::fireReaderPropertiesUpdated); - connect(mSimulatorReader.data(), &SimulatorReader::fireCardInserted, this, &SimulatorReaderManagerPlugin::fireCardInserted); - connect(mSimulatorReader.data(), &SimulatorReader::fireCardRemoved, this, &SimulatorReaderManagerPlugin::fireCardRemoved); - qCDebug(card_simulator) << "fireReaderAdded" << mSimulatorReader->getName(); - Q_EMIT fireReaderAdded(mSimulatorReader->getReaderInfo()); + connect(mReader.data(), &SimulatorReader::fireReaderPropertiesUpdated, this, &SimulatorReaderManagerPlugin::fireReaderPropertiesUpdated); + connect(mReader.data(), &SimulatorReader::fireCardInserted, this, &SimulatorReaderManagerPlugin::fireCardInserted); + connect(mReader.data(), &SimulatorReader::fireCardRemoved, this, &SimulatorReaderManagerPlugin::fireCardRemoved); + qCDebug(card_simulator) << "fireReaderAdded" << mReader->getName(); + Q_EMIT fireReaderAdded(mReader->getReaderInfo()); - mSimulatorReader->connectReader(); + mReader->connectReader(); ReaderManagerPlugin::startScan(pAutoConnect); setInitialScanState(ReaderManagerPluginInfo::InitialScan::SUCCEEDED); } @@ -62,12 +63,12 @@ void SimulatorReaderManagerPlugin::stopScan(const QString& pError) { - if (mSimulatorReader) + if (mReader) { - mSimulatorReader->disconnectReader(pError); + mReader->disconnectReader(pError); - auto info = mSimulatorReader->getReaderInfo(); - mSimulatorReader.reset(); + auto info = mReader->getReaderInfo(); + mReader.reset(); Q_EMIT fireReaderRemoved(info); } ReaderManagerPlugin::stopScan(pError); @@ -83,7 +84,7 @@ return; } - mSimulatorReader->insertCard(pData); + mReader->insertCard(pData); } @@ -97,11 +98,20 @@ qCDebug(card_simulator) << "SimulatorStateChanged:" << enabled; setPluginEnabled(enabled); - if (!enabled && mSimulatorReader) + if (!enabled && mReader) { - mSimulatorReader->disconnect(this); - const auto& readerInfo = mSimulatorReader->getReaderInfo(); - mSimulatorReader.reset(); + mReader->disconnect(this); + const auto readerInfo = mReader->getReaderInfo(); + mReader.reset(); Q_EMIT fireReaderRemoved(readerInfo); } } + + +void SimulatorReaderManagerPlugin::shelveAll() const +{ + if (getInfo().isEnabled() && mReader) + { + shelve(mReader.data()); + } +} diff -Nru ausweisapp2-2.3.1/src/card/simulator/SimulatorReaderManagerPlugin.h ausweisapp2-2.4.0/src/card/simulator/SimulatorReaderManagerPlugin.h --- ausweisapp2-2.3.1/src/card/simulator/SimulatorReaderManagerPlugin.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/simulator/SimulatorReaderManagerPlugin.h 2025-10-30 10:10:48.000000000 +0000 @@ -21,20 +21,22 @@ Q_INTERFACES(governikus::ReaderManagerPlugin) private: - QScopedPointer mSimulatorReader; + QScopedPointer mReader; public: SimulatorReaderManagerPlugin(); - [[nodiscard]] QList getReaders() const override; + [[nodiscard]] QPointer getReader(const QString& pReaderName) const override; void init() override; void startScan(bool pAutoConnect) override; - void stopScan(const QString& pError = QString()) override; + void stopScan(const QString& pError) override; void insert(const QString& pReaderName, const QVariant& pData) override; + void shelveAll() const override; + private Q_SLOTS: void onSettingsChanged(); }; diff -Nru ausweisapp2-2.3.1/src/card/smart/SmartReaderManagerPlugin.cpp ausweisapp2-2.4.0/src/card/smart/SmartReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/src/card/smart/SmartReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/smart/SmartReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -51,20 +51,20 @@ SmartReaderManagerPlugin::SmartReaderManagerPlugin() : ReaderManagerPlugin(ReaderManagerPluginType::SMART) , mReaderAdded(false) - , mSmartReader(nullptr) + , mReader(nullptr) { connect(&Env::getSingleton()->getGeneralSettings(), &GeneralSettings::fireSmartAvailableChanged, this, &SmartReaderManagerPlugin::onSmartAvailableChanged); } -QList SmartReaderManagerPlugin::getReaders() const +QPointer SmartReaderManagerPlugin::getReader(const QString& pReaderName) const { - if (mSmartReader) + if (mReader && mReader->getName() == pReaderName) { - return {mSmartReader.data()}; + return mReader.data(); } - return {}; + return nullptr; } @@ -91,13 +91,13 @@ } setPluginAvailable(true); - mSmartReader.reset(new SmartReader(READER_NAME())); - connect(mSmartReader.data(), &SmartReader::fireReaderPropertiesUpdated, this, &SmartReaderManagerPlugin::fireReaderPropertiesUpdated); - connect(mSmartReader.data(), &SmartReader::fireCardInfoChanged, this, &SmartReaderManagerPlugin::fireCardInfoChanged); - connect(mSmartReader.data(), &SmartReader::fireCardInserted, this, &SmartReaderManagerPlugin::fireCardInserted); - connect(mSmartReader.data(), &SmartReader::fireCardRemoved, this, &SmartReaderManagerPlugin::fireCardRemoved); - qCDebug(card_smart) << "Add reader" << mSmartReader->getName(); - publishReader(mSmartReader->getReaderInfo()); + mReader.reset(new SmartReader(READER_NAME())); + connect(mReader.data(), &SmartReader::fireReaderPropertiesUpdated, this, &SmartReaderManagerPlugin::fireReaderPropertiesUpdated); + connect(mReader.data(), &SmartReader::fireCardInfoChanged, this, &SmartReaderManagerPlugin::fireCardInfoChanged); + connect(mReader.data(), &SmartReader::fireCardInserted, this, &SmartReaderManagerPlugin::fireCardInserted); + connect(mReader.data(), &SmartReader::fireCardRemoved, this, &SmartReaderManagerPlugin::fireCardRemoved); + qCDebug(card_smart) << "Add reader" << mReader->getName(); + publishReader(mReader->getReaderInfo()); } @@ -108,7 +108,7 @@ return; } - mSmartReader.reset(); + mReader.reset(); Q_EMIT fireReaderPropertiesUpdated(ReaderInfo(READER_NAME())); setPluginAvailable(false); } @@ -128,13 +128,13 @@ return; } - if (mSmartReader->getReaderInfo().getName() != pReaderName) + if (mReader->getReaderInfo().getName() != pReaderName) { qCDebug(card_smart) << "Skipping insert because of an unexpected reader name"; return; } - mSmartReader->insertCard(pData); + mReader->insertCard(pData); } @@ -142,7 +142,7 @@ { if (getInfo().isAvailable()) { - mSmartReader->connectReader(); + mReader->connectReader(); ReaderManagerPlugin::startScan(pAutoConnect); } } @@ -152,7 +152,16 @@ { if (getInfo().isAvailable()) { - mSmartReader->disconnectReader(pError); + mReader->disconnectReader(pError); ReaderManagerPlugin::stopScan(pError); } } + + +void SmartReaderManagerPlugin::shelveAll() const +{ + if (mReader) + { + shelve(mReader.data()); + } +} diff -Nru ausweisapp2-2.3.1/src/card/smart/SmartReaderManagerPlugin.h ausweisapp2-2.4.0/src/card/smart/SmartReaderManagerPlugin.h --- ausweisapp2-2.3.1/src/card/smart/SmartReaderManagerPlugin.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/card/smart/SmartReaderManagerPlugin.h 2025-10-30 10:10:48.000000000 +0000 @@ -10,6 +10,9 @@ #include +class test_SmartReaderManagerPlugin; + + namespace governikus { @@ -20,9 +23,11 @@ Q_PLUGIN_METADATA(IID "governikus.ReaderManagerPlugin" FILE "metadata.json") Q_INTERFACES(governikus::ReaderManagerPlugin) + friend class ::test_SmartReaderManagerPlugin; + private: bool mReaderAdded; - QScopedPointer mSmartReader; + QScopedPointer mReader; void publishReader(const ReaderInfo& pInfo); @@ -32,7 +37,7 @@ public: SmartReaderManagerPlugin(); - [[nodiscard]] QList getReaders() const override; + [[nodiscard]] QPointer getReader(const QString& pReaderName) const override; void init() override; void shutdown() override; @@ -40,7 +45,9 @@ void insert(const QString& pReaderName, const QVariant& pData) override; void startScan(bool pAutoConnect) override; - void stopScan(const QString& pError = QString()) override; + void stopScan(const QString& pError) override; + + void shelveAll() const override; }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/configuration/CallCost.cpp ausweisapp2-2.4.0/src/configuration/CallCost.cpp --- ausweisapp2-2.3.1/src/configuration/CallCost.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/configuration/CallCost.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -14,7 +14,7 @@ { inline double getValue(const QJsonValue& pJson, const char* const pSection, const char* const pSubSection) { - return pJson.toObject()[QLatin1String(pSection)].toObject()[QLatin1String(pSubSection)].toDouble(); + return pJson.toObject().value(QLatin1String(pSection)).toObject().value(QLatin1String(pSubSection)).toDouble(); } @@ -31,7 +31,7 @@ CallCost::CallCost(const QJsonValue& pJson) - : mFreeSeconds(pJson.toObject()[QLatin1String("free-seconds")].toInt()) + : mFreeSeconds(pJson.toObject().value(QLatin1String("free-seconds")).toInt()) , mLandlineCentsPerMinute(getValue(pJson, "landline", "per-minute")) , mLandlineCentsPerCall(getValue(pJson, "landline", "per-call")) , mMobileCentsPerMinute(getValue(pJson, "mobile", "per-minute")) diff -Nru ausweisapp2-2.3.1/src/configuration/LanguageString.h ausweisapp2-2.4.0/src/configuration/LanguageString.h --- ausweisapp2-2.3.1/src/configuration/LanguageString.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/configuration/LanguageString.h 2025-10-30 10:10:48.000000000 +0000 @@ -27,8 +27,7 @@ LanguageString(); explicit LanguageString(const QJsonValue& pJson); explicit LanguageString(const QMap& pInput); - LanguageString(const QString& pString, const QLocale& pLocale = LanguageLoader::getInstance().getUsedLocale()); - + explicit LanguageString(const QString& pString, const QLocale& pLocale = LanguageLoader::getInstance().getUsedLocale()); [[nodiscard]] bool isEmpty() const; [[nodiscard]] QString toString() const; diff -Nru ausweisapp2-2.3.1/src/configuration/ProviderConfigurationParser.cpp ausweisapp2-2.4.0/src/configuration/ProviderConfigurationParser.cpp --- ausweisapp2-2.3.1/src/configuration/ProviderConfigurationParser.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/configuration/ProviderConfigurationParser.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -79,7 +79,7 @@ for (const QJsonValueConstRef callCostElem : callCostArray) { const auto cost = CallCost(callCostElem); - const auto& prefixArray = callCostElem.toObject()[QLatin1String("prefixes")].toArray(); + const auto& prefixArray = callCostElem.toObject().value(QLatin1String("prefixes")).toArray(); for (const QJsonValueConstRef prefixElem : prefixArray) { const auto& prefix = prefixElem.toString(); diff -Nru ausweisapp2-2.3.1/src/core/controller/AppController.cpp ausweisapp2-2.4.0/src/core/controller/AppController.cpp --- ausweisapp2-2.3.1/src/core/controller/AppController.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/core/controller/AppController.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -8,14 +8,13 @@ #include "LanguageLoader.h" #include "LogHandler.h" #include "NetworkManager.h" -#include "Randomizer.h" #include "ReaderManager.h" #include "ResourceLoader.h" #include "SecureStorage.h" #include "UiLoader.h" #include "UiPlugin.h" #include "VolatileSettings.h" -#include "controller/ChangePinController.h" +#include "controller/WorkflowController.h" #include #include @@ -56,7 +55,6 @@ QCoreApplication::instance()->installNativeEventFilter(this); #endif - Randomizer::getInstance(); // just init entropy pool Env::getSingleton(); // just init in MainThread because of QObject ResourceLoader::getInstance().init(); @@ -268,6 +266,10 @@ if (newLocale == usedLocale) { + if (QLocale() != usedLocale) + { + QLocale::setDefault(usedLocale); + } return; } @@ -284,6 +286,7 @@ { languageLoader.load(newLocale); } + QLocale::setDefault(languageLoader.getUsedLocale()); Q_EMIT fireTranslationChanged(); } diff -Nru ausweisapp2-2.3.1/src/crypto/Randomizer.cpp ausweisapp2-2.4.0/src/crypto/Randomizer.cpp --- ausweisapp2-2.3.1/src/crypto/Randomizer.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/crypto/Randomizer.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -7,164 +7,19 @@ #include "SingletonHelper.h" #include -#include #include -#include -#include -#include - -#ifdef Q_OS_WIN - #include -#elif defined(Q_OS_UNIX) - #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - #include - #endif - #if defined(SYS_getrandom) - #include - #include - #else - #include - #include - #endif - #if defined(Q_OS_IOS) || defined(Q_OS_MACOS) - #include - #endif -#endif - - using namespace governikus; defineSingleton(Randomizer) -template QList Randomizer::getEntropy() -{ - QList entropy; - - entropy += static_cast(std::chrono::system_clock::now().time_since_epoch().count()); - entropy += std::random_device()(); - entropy += QRandomGenerator::securelySeeded().generate(); - - if (UniversalBuffer buffer; RAND_bytes(buffer.data, sizeof(buffer.data))) - { - entropy += buffer.get(); - } - - entropy += getEntropyWin(); - entropy += getEntropyUnixoid(); - entropy += getEntropyApple(); - - return entropy; -} - - -template QList Randomizer::getEntropyWin() -{ - QList entropy; - -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - UniversalBuffer buffer; - HCRYPTPROV provider = 0; - if (CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - { - if (CryptGenRandom(provider, sizeof(buffer.data), buffer.data)) - { - entropy += buffer.get(); - } - - CryptReleaseContext(provider, 0); - } -#endif - - return entropy; -} - - -template QList Randomizer::getEntropyUnixoid() -{ - QList entropy; - -#ifdef SYS_getrandom - if (UniversalBuffer buffer; syscall(SYS_getrandom, buffer.data, sizeof(buffer.data), GRND_NONBLOCK)) - { - entropy += buffer.get(); - } -#elif defined(Q_OS_UNIX) - // Fallback for unixoid systems without SYS_getrandom (like linux before version 3.17 ). - // This is mainly relevant for older androids. - - UniversalBuffer buffer; - QFile file(QStringLiteral("/dev/urandom")); - if (file.open(QIODevice::ReadOnly)) - { - qint64 bytesToRead = sizeof(buffer.data); - do - { - const qint64 bytesRead = file.read(buffer.data, bytesToRead); - if (bytesRead < 0) - { - qCritical() << "Failed to read" << file.fileName(); - break; - } - bytesToRead -= bytesRead; - } - while (bytesToRead > 0); - - entropy += buffer.get(); - file.close(); - } - else - { - qCritical() << "Failed to open" << file.fileName(); - } -#endif - - return entropy; -} - - -template QList Randomizer::getEntropyApple() -{ - QList entropy; - -#if defined(Q_OS_IOS) || defined(Q_OS_MACOS) - if (UniversalBuffer buffer; SecRandomCopyBytes(kSecRandomDefault, sizeof(buffer.data), buffer.data) == 0) - { - entropy += buffer.get(); - } -#endif - - return entropy; -} - - -Randomizer::Randomizer() -{ - const auto& entropy = getEntropy(); - std::seed_seq seed(entropy.cbegin(), entropy.cend()); - mGenerator.seed(seed); - - // We need to seed pseudo random pool of openssl. - // yes, OpenSSL is an entropy source, too. But not the only one! - UniversalBuffer buffer; - buffer.set(mGenerator()); - RAND_seed(buffer.data, sizeof(std::mt19937_64::result_type)); - - static const int MINIMUM_ENTROPY_SOURCES = 4; - mSecureRandom = seed.size() >= MINIMUM_ENTROPY_SOURCES; -} - - -std::mt19937_64& Randomizer::getGenerator() -{ - return mGenerator; -} +Randomizer::Randomizer() = default; -bool Randomizer::isSecureRandom() const +QRandomGenerator* Randomizer::getGenerator(bool pSystem) { - return mSecureRandom; + return pSystem ? QRandomGenerator::system() : QRandomGenerator::global(); } @@ -172,11 +27,12 @@ { std::uniform_int_distribution uni; + auto* generator = getGenerator(); QByteArray randomBytes; while (randomBytes.size() < pCount) { std::array number; - qToBigEndian(uni(mGenerator), number.data()); + qToBigEndian(uni(*generator), number.data()); randomBytes.append(number.data(), number.size()); } return randomBytes.first(pCount); diff -Nru ausweisapp2-2.3.1/src/crypto/Randomizer.h ausweisapp2-2.4.0/src/crypto/Randomizer.h --- ausweisapp2-2.3.1/src/crypto/Randomizer.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/crypto/Randomizer.h 2025-10-30 10:10:48.000000000 +0000 @@ -4,12 +4,9 @@ #pragma once -#include +#include #include -#include - - class test_Randomizer; @@ -21,42 +18,6 @@ Q_DISABLE_COPY(Randomizer) friend class ::test_Randomizer; - private: - template struct UniversalBuffer - { - U data[sizeof(T)] = {}; - - T get() - { -#if __cpp_lib_bit_cast >= 201806 - return std::bit_cast(data); - -#else - T number; - memcpy(&number, &data, sizeof(T)); - return number; - -#endif - } - - - void set(T pNumber) - { - memcpy(&data, &pNumber, sizeof(T)); - } - - - static_assert(sizeof(T) == sizeof(data)); - }; - - std::mt19937_64 mGenerator; - bool mSecureRandom; - - template static QList getEntropy(); - template static QList getEntropyWin(); - template static QList getEntropyUnixoid(); - template static QList getEntropyApple(); - protected: Randomizer(); ~Randomizer() = default; @@ -64,8 +25,7 @@ public: static Randomizer& getInstance(); - [[nodiscard]] std::mt19937_64& getGenerator(); - [[nodiscard]] bool isSecureRandom() const; + [[nodiscard]] QRandomGenerator* getGenerator(bool pSystem = true); [[nodiscard]] QByteArray createBytes(int pCount); [[nodiscard]] QUuid createUuid(); diff -Nru ausweisapp2-2.3.1/src/diagnosis/DiagnosisAntivirusDetection.cpp ausweisapp2-2.4.0/src/diagnosis/DiagnosisAntivirusDetection.cpp --- ausweisapp2-2.3.1/src/diagnosis/DiagnosisAntivirusDetection.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/diagnosis/DiagnosisAntivirusDetection.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -91,7 +91,7 @@ #endif -const QList>& DiagnosisAntivirusDetection::getAntivirusInformations() const +const QList>& DiagnosisAntivirusDetection::getAntivirusInformation() const { return mAntivirInfos; } diff -Nru ausweisapp2-2.3.1/src/diagnosis/DiagnosisAntivirusDetection.h ausweisapp2-2.4.0/src/diagnosis/DiagnosisAntivirusDetection.h --- ausweisapp2-2.3.1/src/diagnosis/DiagnosisAntivirusDetection.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/diagnosis/DiagnosisAntivirusDetection.h 2025-10-30 10:10:48.000000000 +0000 @@ -83,7 +83,7 @@ #endif - [[nodiscard]] const QList>& getAntivirusInformations() const; + [[nodiscard]] const QList>& getAntivirusInformation() const; }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/diagnosis/DiagnosisFirewallDetection.cpp ausweisapp2-2.4.0/src/diagnosis/DiagnosisFirewallDetection.cpp --- ausweisapp2-2.3.1/src/diagnosis/DiagnosisFirewallDetection.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/diagnosis/DiagnosisFirewallDetection.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -51,9 +51,9 @@ { continue; } - if (lineparts[0].startsWith(QLatin1String("Name"))) + if (lineparts[0].startsWith(QLatin1String("DisplayName"))) { - if (lineparts[1].startsWith(QLatin1String("AusweisApp-Firewall-Rule"))) + if (lineparts[1].startsWith(QLatin1String("AusweisApp"))) { mFirstFirewallRuleExists = true; } @@ -104,7 +104,7 @@ { continue; } - if (lineparts[0].startsWith(QLatin1String("Name")) || lineparts[0].startsWith(QLatin1String("DisplayName"))) + if (lineparts[0].startsWith(QLatin1String("DisplayName"))) { if (lineparts[1].startsWith(QLatin1String("AusweisApp-SaC"))) { @@ -321,7 +321,7 @@ QStringList firstRuleParameters; firstRuleParameters << QStringLiteral("-NoProfile"); firstRuleParameters << QStringLiteral("-Command"); - firstRuleParameters << QStringLiteral("Get-NetFirewallRule -Name \"AusweisApp-Firewall-Rule\""); + firstRuleParameters << QStringLiteral("Get-NetFirewallRule -DisplayName \"AusweisApp\""); connect(&mFirewallFirstRuleProcess, QOverload::of(&QProcess::finished), this, &DiagnosisFirewallDetection::onFirstRuleDone); connect(&mFirewallFirstRuleProcess, &QProcess::errorOccurred, this, &DiagnosisFirewallDetection::onFirstRuleError); diff -Nru ausweisapp2-2.3.1/src/diagnosis/DiagnosisModel.cpp ausweisapp2-2.4.0/src/diagnosis/DiagnosisModel.cpp --- ausweisapp2-2.3.1/src/diagnosis/DiagnosisModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/diagnosis/DiagnosisModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -536,7 +536,7 @@ mAntivirusSection.clear(); mAntivirusSectionRunning = false; - const auto& antivirusInfos = mAntivirusDetection.getAntivirusInformations(); + const auto& antivirusInfos = mAntivirusDetection.getAntivirusInformation(); if (antivirusInfos.isEmpty()) { //: LABEL DESKTOP diff -Nru ausweisapp2-2.3.1/src/diagnosis/DiagnosisModel.h ausweisapp2-2.4.0/src/diagnosis/DiagnosisModel.h --- ausweisapp2-2.3.1/src/diagnosis/DiagnosisModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/diagnosis/DiagnosisModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -99,7 +99,7 @@ ~DiagnosisModel() override; [[nodiscard]] QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; - [[nodiscard]] int rowCount(const QModelIndex& pParent = QModelIndex()) const override; + [[nodiscard]] int rowCount(const QModelIndex& pParent) const override; [[nodiscard]] QHash roleNames() const override; [[nodiscard]] Q_INVOKABLE QString getCreationTime() const; diff -Nru ausweisapp2-2.3.1/src/diagnosis/SectionModel.h ausweisapp2-2.4.0/src/diagnosis/SectionModel.h --- ausweisapp2-2.3.1/src/diagnosis/SectionModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/diagnosis/SectionModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -40,7 +40,7 @@ explicit SectionModel(QObject* pParent = nullptr); [[nodiscard]] QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; - [[nodiscard]] int rowCount(const QModelIndex& pParent = QModelIndex()) const override; + [[nodiscard]] int rowCount(const QModelIndex& pParent) const override; [[nodiscard]] QHash roleNames() const override; void removeAllItems(); diff -Nru ausweisapp2-2.3.1/src/diagnosis/context/DiagnosisContext.h ausweisapp2-2.4.0/src/diagnosis/context/DiagnosisContext.h --- ausweisapp2-2.3.1/src/diagnosis/context/DiagnosisContext.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/diagnosis/context/DiagnosisContext.h 2025-10-30 10:10:48.000000000 +0000 @@ -103,7 +103,7 @@ QString mManufacturer; public: - ComponentInfo(const QString& pPath = QString(), const QString& pDescription = QString(), + explicit ComponentInfo(const QString& pPath = QString(), const QString& pDescription = QString(), const QString& pVersion = QString(), const QString& pManufacturer = QString()) : mPath(pPath) , mDescription(pDescription) diff -Nru ausweisapp2-2.3.1/src/diagnosis/controller/DiagnosisController.cpp ausweisapp2-2.4.0/src/diagnosis/controller/DiagnosisController.cpp --- ausweisapp2-2.3.1/src/diagnosis/controller/DiagnosisController.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/diagnosis/controller/DiagnosisController.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -106,14 +106,9 @@ } auto attachedDevices = Env::getSingleton()->getAttachedSupportedDevices(); - QMutableListIterator iter(attachedDevices); - while (iter.hasNext()) - { - if (readersWithDriverInfos.contains(iter.next())) - { - iter.remove(); - } - } + erase_if(attachedDevices, [&readersWithDriverInfos](const auto& pInfo){ + return readersWithDriverInfos.contains(pInfo); + }); mContext->setReaderInfosNoDriver(attachedDevices); diff -Nru ausweisapp2-2.3.1/src/diagnosis/controller/DiagnosisController_osx.mm ausweisapp2-2.4.0/src/diagnosis/controller/DiagnosisController_osx.mm --- ausweisapp2-2.3.1/src/diagnosis/controller/DiagnosisController_osx.mm 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/diagnosis/controller/DiagnosisController_osx.mm 2025-10-30 10:10:48.000000000 +0000 @@ -117,11 +117,11 @@ } -static QString runProcessAndReadAllOutput(const QString& pProgramm, const QStringList& pArguments) +static QString runProcessAndReadAllOutput(const QString& pProgram, const QStringList& pArguments) { QString allOutput; QProcess process; - process.start(pProgramm, pArguments); + process.start(pProgram, pArguments); while (process.waitForReadyRead()) { allOutput += QString::fromUtf8(process.readAllStandardOutput()); diff -Nru ausweisapp2-2.3.1/src/file_provider/Downloader.cpp ausweisapp2-2.4.0/src/file_provider/Downloader.cpp --- ausweisapp2-2.3.1/src/file_provider/Downloader.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/file_provider/Downloader.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -100,7 +99,7 @@ lastModified = QDateTime::currentDateTime(); } - if (const auto& readData = mCurrentReply->readAll(); !hasError && readData.size() > 0) + if (const auto& readData = mCurrentReply->readAll(); !hasError && !readData.isEmpty()) { Q_EMIT fireDownloadSuccess(url, lastModified, readData); return; @@ -169,21 +168,19 @@ aborted = true; } - QMutableListIterator iterator(mPendingRequests); - while (iterator.hasNext()) - { - const auto item = iterator.next(); - if (item.url() == pUpdateUrl) - { - qCDebug(fileprovider) << "Remove pending request"; + const auto removed = erase_if(mPendingRequests, [&pUpdateUrl, this](const auto& pRequest){ + if (pRequest.url() == pUpdateUrl) + { + qCDebug(fileprovider) << "Remove pending request"; + + Q_EMIT fireDownloadFailed(pUpdateUrl, GlobalStatus::Code::Downloader_Aborted); + return true; + } - Q_EMIT fireDownloadFailed(pUpdateUrl, GlobalStatus::Code::Downloader_Aborted); - aborted = true; - iterator.remove(); - } - } + return false; + }); - return aborted; + return aborted || (removed > 0); } diff -Nru ausweisapp2-2.3.1/src/file_provider/UpdatableFile.cpp ausweisapp2-2.4.0/src/file_provider/UpdatableFile.cpp --- ausweisapp2-2.3.1/src/file_provider/UpdatableFile.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/file_provider/UpdatableFile.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -259,11 +259,11 @@ QString path = lookupPath(); if (path.startsWith(QLatin1String(":/"))) { - return QStringLiteral("qrc://") + path.remove(0, 1); + return QUrl(QStringLiteral("qrc://") + path.remove(0, 1)); } else { - return QStringLiteral("file:///") + path; + return QUrl(QStringLiteral("file:///") + path); } } diff -Nru ausweisapp2-2.3.1/src/global/BuildHelper.cpp ausweisapp2-2.4.0/src/global/BuildHelper.cpp --- ausweisapp2-2.3.1/src/global/BuildHelper.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/BuildHelper.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -13,9 +13,6 @@ #include #endif -#ifdef Q_OS_IOS - #include -#endif #include #include @@ -152,29 +149,12 @@ #endif -QString getSystemName() -{ -#ifdef Q_OS_IOS - const auto& osVersion = QOperatingSystemVersion::current(); - return QStringLiteral("%1 (%2.%3.%4)").arg( - osVersion.name()).arg( - osVersion.majorVersion()).arg( - osVersion.minorVersion()).arg( - osVersion.microVersion()); - -#else - return QSysInfo::prettyProductName(); - -#endif -} - - -QList> BuildHelper::getInformationHeader() +QList> BuildHelper::getInformationHeader() { - QList> data; + QList> data; const auto& add = [&data](const char* pKey, const QString& pStr) { - data << qMakePair(QLatin1String(pKey), pStr); + data << std::make_pair(QLatin1String(pKey), pStr); }; add(QT_TR_NOOP("Application"), QCoreApplication::applicationName()); @@ -182,7 +162,7 @@ add(QT_TR_NOOP("Organization"), QCoreApplication::organizationName()); add(QT_TR_NOOP("Organization Domain"), QCoreApplication::organizationDomain()); - add(QT_TR_NOOP("System"), getSystemName()); + add(QT_TR_NOOP("System"), QSysInfo::prettyProductName()); add(QT_TR_NOOP("Kernel"), QSysInfo::kernelVersion()); QString architecture = QSysInfo::currentCpuArchitecture(); diff -Nru ausweisapp2-2.3.1/src/global/BuildHelper.h ausweisapp2-2.4.0/src/global/BuildHelper.h --- ausweisapp2-2.3.1/src/global/BuildHelper.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/BuildHelper.h 2025-10-30 10:10:48.000000000 +0000 @@ -8,7 +8,6 @@ #include #include -#include #include #ifdef Q_OS_ANDROID @@ -17,7 +16,7 @@ #endif #include - +#include namespace governikus { @@ -40,7 +39,7 @@ [[nodiscard]] static bool fetchUserInteractive(); public: - static QList> getInformationHeader(); + static QList> getInformationHeader(); static void processInformationHeader(const std::function& pFunc, bool pTranslate = true); [[nodiscard]] static CertificateType getCertificateType(); diff -Nru ausweisapp2-2.3.1/src/global/ECardApiResult.cpp ausweisapp2-2.4.0/src/global/ECardApiResult.cpp --- ausweisapp2-2.3.1/src/global/ECardApiResult.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/ECardApiResult.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -9,8 +9,10 @@ #include #include + using namespace governikus; + constexpr const char* RESULTMAJOR = "http://www.bsi.bund.de/ecard/api/1.1/resultmajor"; constexpr const char* RESULTMINOR = "http://www.bsi.bund.de/ecard/api/1.1/resultminor"; @@ -114,7 +116,6 @@ addConversionElement(GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::IfdConnector_InvalidRequest, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::IfdConnector_NoSupportedApiLevel, Minor::AL_Unknown_Error); - addConversionElement(GlobalStatus::Code::IfdConnector_ConnectionTimeout, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::IfdConnector_ConnectionError, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::IfdConnector_RemoteHostRefusedConnection, Minor::AL_Unknown_Error); addConversionElement(GlobalStatus::Code::Downloader_File_Not_Found, Minor::AL_Unknown_Error); diff -Nru ausweisapp2-2.3.1/src/global/ECardApiResult.h ausweisapp2-2.4.0/src/global/ECardApiResult.h --- ausweisapp2-2.3.1/src/global/ECardApiResult.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/ECardApiResult.h 2025-10-30 10:10:48.000000000 +0000 @@ -15,10 +15,11 @@ #include #include #include -#include #include #include + #include +#include class test_ECardApiResult; diff -Nru ausweisapp2-2.3.1/src/global/Env.h ausweisapp2-2.4.0/src/global/Env.h --- ausweisapp2-2.3.1/src/global/Env.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/Env.h 2025-10-30 10:10:48.000000000 +0000 @@ -372,18 +372,17 @@ auto& holder = getInstance(); const QWriteLocker locker(&holder.mLock); - QMutableListIterator iter(holder.mInstancesCreator); - while (iter.hasNext()) + auto& creator = holder.mInstancesCreator; + for (auto iter = creator.begin(); iter != creator.end(); ++iter) { - iter.next(); - if (iter.value().dynamicCast>()) + if (iter->dynamicCast>()) { - iter.setValue(value); + *iter = value; return; } } - holder.mInstancesCreator << value; + creator << value; } diff -Nru ausweisapp2-2.3.1/src/global/FailureCode.cpp ausweisapp2-2.4.0/src/global/FailureCode.cpp --- ausweisapp2-2.3.1/src/global/FailureCode.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/FailureCode.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -24,7 +24,7 @@ } -FailureCode::FailureCode(Reason pReason, const QPair& pInfo) +FailureCode::FailureCode(Reason pReason, const std::pair& pInfo) : mReason(pReason) , mFailureInfoMap({ {pInfo.first, pInfo.second} diff -Nru ausweisapp2-2.3.1/src/global/FailureCode.h ausweisapp2-2.4.0/src/global/FailureCode.h --- ausweisapp2-2.3.1/src/global/FailureCode.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/FailureCode.h 2025-10-30 10:10:48.000000000 +0000 @@ -8,7 +8,8 @@ #include #include -#include + +#include namespace governikus @@ -184,7 +185,7 @@ FailureCode(Reason pReason); FailureCode(Reason pReason, const FailureInfoMap& pInfoMap); - FailureCode(Reason pReason, const QPair& pInfo); + FailureCode(Reason pReason, const std::pair& pInfo); [[nodiscard]] Reason getReason() const; [[nodiscard]] bool operator==(const FailureCode& pFailure) const; diff -Nru ausweisapp2-2.3.1/src/global/GlobalStatus.cpp ausweisapp2-2.4.0/src/global/GlobalStatus.cpp --- ausweisapp2-2.3.1/src/global/GlobalStatus.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/GlobalStatus.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -9,8 +9,10 @@ #include + using namespace governikus; + INIT_FUNCTION([] { qRegisterMetaType("GlobalStatus::Code"); qRegisterMetaType("GlobalStatus"); @@ -123,7 +125,7 @@ case Code::Workflow_Card_Removed: //: 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."); + return tr("Restart the authentication process and make sure that the position of the ID card does not change during the reading process."); 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. @@ -403,10 +405,6 @@ //: ERROR ALL_PLATFORMS The requested connection to the smartphone card reader (SaK) was invalid (API mismatch). return tr("Your smartphone as card reader (SaC) version is incompatible with the local version. Please install the latest %1 version on both your smartphone and your computer.").arg(QCoreApplication::applicationName()); - case Code::IfdConnector_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 the smartphone as card reader (SaC)."); - case Code::IfdConnector_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 the smartphone as card reader (SaC)."); diff -Nru ausweisapp2-2.3.1/src/global/GlobalStatus.h ausweisapp2-2.4.0/src/global/GlobalStatus.h --- ausweisapp2-2.3.1/src/global/GlobalStatus.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/GlobalStatus.h 2025-10-30 10:10:48.000000000 +0000 @@ -8,12 +8,119 @@ #include #include -#include #include +#include + namespace governikus { +defineEnumTypeQmlExposed(GlobalStatusCode, + Unknown_Error, + No_Error, + + Network_ServiceUnavailable, + Network_ServerError, + Network_ClientError, + Network_Ssl_Establishment_Error, + Network_TimeOut, + Network_Proxy_Error, + Network_Other_Error, + + Downloader_File_Not_Found, + Downloader_Cannot_Save_File, + Downloader_Data_Corrupted, + Downloader_Missing_Platform, + Downloader_Aborted, + + Update_Execution_Failed, + + Workflow_AlreadyInProgress_Error, + Workflow_Communication_Missing_Redirect_Url, + Workflow_InternalError_BeforeTcToken, + Workflow_Cancellation_By_User, + Workflow_Card_Removed, + Workflow_Cannot_Confirm_IdCard_Authenticity, + Workflow_Unknown_Paos_From_EidServer, + Workflow_Start_Paos_Response_Missing, + Workflow_Unexpected_Message_From_EidServer, + Workflow_Preverification_Developermode_Error, + Workflow_Preverification_Error, + Workflow_No_Unique_AtCvc, + Workflow_No_Unique_DvCvc, + Workflow_No_Permission_Error, + Workflow_Wrong_Length_Error, + Workflow_Certificate_No_Description, + Workflow_Certificate_No_Url_In_Description, + Workflow_Certificate_Hash_Error, + Workflow_Certificate_Sop_Error, + Workflow_Browser_Transmission_Error, + Workflow_TrustedChannel_Establishment_Error, + Workflow_TrustedChannel_Server_Error, + Workflow_TrustedChannel_Client_Error, + Workflow_TrustedChannel_Hash_Not_In_Description, + Workflow_TrustedChannel_No_Data_Received, + Workflow_TrustedChannel_Ssl_Connection_Unsupported_Algorithm_Or_Length, + Workflow_TrustedChannel_Ssl_Certificate_Unsupported_Algorithm_Or_Length, + Workflow_TrustedChannel_ServiceUnavailable, + Workflow_TrustedChannel_TimeOut, + Workflow_TrustedChannel_Proxy_Error, + Workflow_TrustedChannel_Server_Format_Error, + Workflow_TrustedChannel_Other_Network_Error, + Workflow_Reader_Became_Inaccessible, + Workflow_Server_Incomplete_Information_Provided, + Workflow_Network_Ssl_Connection_Unsupported_Algorithm_Or_Length, + Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length, + Workflow_Network_Ssl_Hash_Not_In_Certificate_Description, + Workflow_Network_Empty_Redirect_Url, + Workflow_Network_Expected_Redirect, + Workflow_Network_Invalid_Scheme, + Workflow_Network_Malformed_Redirect_Url, + Workflow_Wrong_Parameter_Invocation, + Workflow_Smart_eID_Unavailable, + Workflow_Smart_eID_Applet_Preparation_Failed, + Workflow_Smart_eID_Authentication_Failed, + Workflow_Smart_eID_ServiceInformation_Query_Failed, + Workflow_Smart_eID_PrePersonalization_Failed, + Workflow_Smart_eID_Personalization_Failed, + Workflow_Smart_eID_Personalization_Denied, + + Paos_Unexpected_Warning, + + Paos_Generic_Server_Error, + + Paos_Error_AL_Unknown_Error, + Paos_Error_AL_Internal_Error, + Paos_Error_AL_Communication_Error, + Paos_Error_DP_Trusted_Channel_Establishment_Failed, + Paos_Error_SAL_Cancellation_by_User, + Paos_Error_SAL_Invalid_Key, + + Card_Not_Found, + Card_Communication_Error, + Card_Protocol_Error, + Card_Unexpected_Transmit_Status, + Card_Cancellation_By_User, + Card_Input_TimeOut, + Card_Pin_Deactivated, + Card_Invalid_Pin, + Card_Invalid_Can, + Card_Invalid_Puk, + Card_Pin_Blocked, + Card_Pin_Not_Blocked, + Card_Puk_Blocked, + Card_NewPin_Mismatch, + Card_NewPin_Invalid_Length, + Card_ValidityVerificationFailed, + Card_Smart_Invalid, + + RemoteReader_CloseCode_AbnormalClose, + + IfdConnector_InvalidRequest, + IfdConnector_NoSupportedApiLevel, + IfdConnector_ConnectionError, + IfdConnector_RemoteHostRefusedConnection + ) class GlobalStatus { @@ -21,114 +128,7 @@ Q_DECLARE_TR_FUNCTIONS(governikus::GlobalStatus) public: - enum class Code - { - Unknown_Error, - No_Error, - - Network_ServiceUnavailable, - Network_ServerError, - Network_ClientError, - Network_Ssl_Establishment_Error, - Network_TimeOut, - Network_Proxy_Error, - Network_Other_Error, - - Downloader_File_Not_Found, - Downloader_Cannot_Save_File, - Downloader_Data_Corrupted, - Downloader_Missing_Platform, - Downloader_Aborted, - - Update_Execution_Failed, - - Workflow_AlreadyInProgress_Error, - Workflow_Communication_Missing_Redirect_Url, - Workflow_InternalError_BeforeTcToken, - Workflow_Cancellation_By_User, - Workflow_Card_Removed, - Workflow_Cannot_Confirm_IdCard_Authenticity, - Workflow_Unknown_Paos_From_EidServer, - Workflow_Start_Paos_Response_Missing, - Workflow_Unexpected_Message_From_EidServer, - Workflow_Preverification_Developermode_Error, - Workflow_Preverification_Error, - Workflow_No_Unique_AtCvc, - Workflow_No_Unique_DvCvc, - Workflow_No_Permission_Error, - Workflow_Wrong_Length_Error, - Workflow_Certificate_No_Description, - Workflow_Certificate_No_Url_In_Description, - Workflow_Certificate_Hash_Error, - Workflow_Certificate_Sop_Error, - Workflow_Browser_Transmission_Error, - Workflow_TrustedChannel_Establishment_Error, - Workflow_TrustedChannel_Server_Error, - Workflow_TrustedChannel_Client_Error, - Workflow_TrustedChannel_Hash_Not_In_Description, - Workflow_TrustedChannel_No_Data_Received, - Workflow_TrustedChannel_Ssl_Connection_Unsupported_Algorithm_Or_Length, - Workflow_TrustedChannel_Ssl_Certificate_Unsupported_Algorithm_Or_Length, - Workflow_TrustedChannel_ServiceUnavailable, - Workflow_TrustedChannel_TimeOut, - Workflow_TrustedChannel_Proxy_Error, - Workflow_TrustedChannel_Server_Format_Error, - Workflow_TrustedChannel_Other_Network_Error, - Workflow_Reader_Became_Inaccessible, - Workflow_Server_Incomplete_Information_Provided, - Workflow_Network_Ssl_Connection_Unsupported_Algorithm_Or_Length, - Workflow_Network_Ssl_Certificate_Unsupported_Algorithm_Or_Length, - Workflow_Network_Ssl_Hash_Not_In_Certificate_Description, - Workflow_Network_Empty_Redirect_Url, - Workflow_Network_Expected_Redirect, - Workflow_Network_Invalid_Scheme, - Workflow_Network_Malformed_Redirect_Url, - Workflow_Wrong_Parameter_Invocation, - Workflow_Smart_eID_Unavailable, - Workflow_Smart_eID_Applet_Preparation_Failed, - Workflow_Smart_eID_Authentication_Failed, - Workflow_Smart_eID_ServiceInformation_Query_Failed, - Workflow_Smart_eID_PrePersonalization_Failed, - Workflow_Smart_eID_Personalization_Failed, - Workflow_Smart_eID_Personalization_Denied, - - Paos_Unexpected_Warning, - - Paos_Generic_Server_Error, - - Paos_Error_AL_Unknown_Error, - Paos_Error_AL_Internal_Error, - Paos_Error_AL_Communication_Error, - Paos_Error_DP_Trusted_Channel_Establishment_Failed, - Paos_Error_SAL_Cancellation_by_User, - Paos_Error_SAL_Invalid_Key, - - Card_Not_Found, - Card_Communication_Error, - Card_Protocol_Error, - Card_Unexpected_Transmit_Status, - Card_Cancellation_By_User, - Card_Input_TimeOut, - Card_Pin_Deactivated, - Card_Invalid_Pin, - Card_Invalid_Can, - Card_Invalid_Puk, - Card_Pin_Blocked, - Card_Pin_Not_Blocked, - Card_Puk_Blocked, - Card_NewPin_Mismatch, - Card_NewPin_Invalid_Length, - Card_ValidityVerificationFailed, - Card_Smart_Invalid, - - RemoteReader_CloseCode_AbnormalClose, - - IfdConnector_InvalidRequest, - IfdConnector_NoSupportedApiLevel, - IfdConnector_ConnectionTimeout, - IfdConnector_ConnectionError, - IfdConnector_RemoteHostRefusedConnection - }; + using Code = GlobalStatusCode; enum class Origin { @@ -171,7 +171,7 @@ } - InternalStatus(Code pStatusCode, const QPair& pExternalInformation, const Origin pOrigin) + InternalStatus(Code pStatusCode, const std::pair& pExternalInformation, const Origin pOrigin) : mStatusCode(pStatusCode) , mExternalInformation({ {pExternalInformation.first, pExternalInformation.second} @@ -203,7 +203,7 @@ } - GlobalStatus(Code pStatusCode, const QPair& pExternalInformation, const Origin pOrigin = Origin::Client) + GlobalStatus(Code pStatusCode, const std::pair& pExternalInformation, const Origin pOrigin = Origin::Client) : d(new InternalStatus(pStatusCode, pExternalInformation, pOrigin)) { } @@ -234,8 +234,6 @@ using Origin = GlobalStatus::Origin; -defineEnumOperators(GlobalStatus::Code) - } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/global/LanguageLoader.cpp ausweisapp2-2.4.0/src/global/LanguageLoader.cpp --- ausweisapp2-2.3.1/src/global/LanguageLoader.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/LanguageLoader.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -47,12 +47,6 @@ } -void LanguageLoader::setDefaultLanguage(const QLocale& pLocale) -{ - mDefaultLanguage = pLocale; -} - - QString LanguageLoader::getLocaleCode(const QLocale& pLocale) { return pLocale.bcp47Name().left(2); diff -Nru ausweisapp2-2.3.1/src/global/LanguageLoader.h ausweisapp2-2.4.0/src/global/LanguageLoader.h --- ausweisapp2-2.3.1/src/global/LanguageLoader.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/LanguageLoader.h 2025-10-30 10:10:48.000000000 +0000 @@ -35,14 +35,13 @@ bool loadTranslationFiles(const QLocale& pLocale); QSharedPointer createTranslator(const QLocale& pLocale, const QString& pComponent); + [[nodiscard]] static const QLocale& getDefaultLanguage(); protected: LanguageLoader(); ~LanguageLoader() = default; public: - [[nodiscard]] static const QLocale& getDefaultLanguage(); - static void setDefaultLanguage(const QLocale& pLocale); static LanguageLoader& getInstance(); static QString getLocaleCode(const QLocale& pLocale = getInstance().getUsedLocale()); diff -Nru ausweisapp2-2.3.1/src/global/LogHandler.h ausweisapp2-2.4.0/src/global/LogHandler.h --- ausweisapp2-2.3.1/src/global/LogHandler.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/LogHandler.h 2025-10-30 10:10:48.000000000 +0000 @@ -25,6 +25,7 @@ class test_LogHandler; class test_LogModel; +class test_LogFilesModel; namespace governikus @@ -71,6 +72,7 @@ friend class Env; friend class ::test_LogHandler; friend class ::test_LogModel; + friend class ::test_LogFilesModel; friend QDebug operator<<(QDebug, const LogHandler&); private: diff -Nru ausweisapp2-2.3.1/src/global/UsbId.h ausweisapp2-2.4.0/src/global/UsbId.h --- ausweisapp2-2.3.1/src/global/UsbId.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/global/UsbId.h 2025-10-30 10:10:48.000000000 +0000 @@ -17,7 +17,7 @@ unsigned int mProductId; public: - UsbId(unsigned int pVendorId = 0x0, unsigned int pProductId = 0x0); + explicit UsbId(unsigned int pVendorId = 0x0, unsigned int pProductId = 0x0); [[nodiscard]] unsigned int getVendorId() const; [[nodiscard]] unsigned int getProductId() const; diff -Nru ausweisapp2-2.3.1/src/ifd/base/ConnectRequest.cpp ausweisapp2-2.4.0/src/ifd/base/ConnectRequest.cpp --- ausweisapp2-2.3.1/src/ifd/base/ConnectRequest.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/ConnectRequest.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -14,20 +14,23 @@ #include #include + Q_DECLARE_LOGGING_CATEGORY(ifd) + using namespace governikus; -ConnectRequest::ConnectRequest(const IfdDescriptor& pIfdDescriptor, +ConnectRequest::ConnectRequest(const Discovery& pDiscovery, const QByteArray& pPsk, int pTimeoutMs) - : mIfdDescriptor(pIfdDescriptor) + : mDiscovery(pDiscovery) , mPsk(pPsk) - , mSocket(new QWebSocket(), &QObject::deleteLater) + , mSockets() , mTimer() + , mRemoteHostRefusedConnection(false) { - if (mIfdDescriptor.isLocalIfd()) + if (mDiscovery.isLocalIfd()) { if (mPsk.isEmpty()) { @@ -45,33 +48,19 @@ } } - setTlsConfiguration(); - - connect(mSocket.data(), &QWebSocket::preSharedKeyAuthenticationRequired, this, &ConnectRequest::onPreSharedKeyAuthenticationRequired); - connect(mSocket.data(), &QWebSocket::sslErrors, this, &ConnectRequest::onSslErrors); - - mSocket->setProxy(QNetworkProxy(QNetworkProxy::NoProxy)); - connect(mSocket.data(), &QWebSocket::connected, this, &ConnectRequest::onConnected); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) - connect(mSocket.data(), &QWebSocket::errorOccurred, this, &ConnectRequest::onError); -#else - connect(mSocket.data(), QOverload::of(&QWebSocket::error), this, &ConnectRequest::onError); -#endif - mTimer.setSingleShot(true); mTimer.setInterval(pTimeoutMs); connect(&mTimer, &QTimer::timeout, this, &ConnectRequest::onTimeout); } -void ConnectRequest::setTlsConfiguration() const +QSslConfiguration ConnectRequest::getTlsConfiguration() const { QSslConfiguration config; - if (mIfdDescriptor.isLocalIfd()) + if (mDiscovery.isLocalIfd()) { config = Env::getSingleton()->getTlsConfigLocalIfd().getConfiguration(); - qCInfo(ifd) << "Start local connection"; } else { @@ -80,12 +69,10 @@ { config = Env::getSingleton()->getTlsConfigRemoteIfd().getConfiguration(); config.setCaCertificates(remoteServiceSettings.getTrustedCertificates()); - qCInfo(ifd) << "Start reconnect to server"; } else { config = Env::getSingleton()->getTlsConfigRemoteIfd(SecureStorage::TlsSuite::PSK).getConfiguration(); - qCInfo(ifd) << "Start pairing to server"; } config.setPrivateKey(remoteServiceSettings.getKey()); @@ -93,15 +80,75 @@ config.setPeerVerifyMode(QSslSocket::VerifyPeer); } - mSocket->setSslConfiguration(config); + return config; } -void ConnectRequest::onConnected() +QAbstractSocket::SocketState ConnectRequest::getState(const QSharedPointer& pSocket) const { + if (!pSocket) + { + return QAbstractSocket::UnconnectedState; + } + + return pSocket->state(); +} + + +void ConnectRequest::processResult(const QSharedPointer& pSocket) +{ + const auto state = getState(pSocket); + if (pSocket) + { + qCDebug(ifd) << " Connection to" << pSocket->requestUrl() << "finished with" << state; + if (!mSockets.removeOne(pSocket)) + { + qCWarning(ifd) << "Ignoring result from unexpected socket. ConnectRequest has probably already been completed"; + return; + } + + if (state != QAbstractSocket::ConnectedState && !mSockets.isEmpty()) + { + qCDebug(ifd) << " Connection failed. Waiting for pending connections"; + return; + } + } + mTimer.stop(); + for (const auto& socket : std::as_const(mSockets)) + { + socket->abort(); + } + mSockets.clear(); + + if (pSocket && state == QAbstractSocket::ConnectedState) + { + qCDebug(ifd) << " Connection succeeded"; + Q_EMIT fireConnectionCreated(this, pSocket); + return; + } + + if (pSocket) + { + qCDebug(ifd) << " Connection failed. No more pending connections left"; + } + else + { + qCWarning(ifd) << "Connection could not be established after" << mTimer.interval() << "ms"; + } + if (mRemoteHostRefusedConnection) + { + Q_EMIT fireConnectionError(this, IfdErrorCode::REMOTE_HOST_REFUSED_CONNECTION); + return; + } + + Q_EMIT fireConnectionError(this, IfdErrorCode::CONNECTION_ERROR); +} + - const auto& cfg = mSocket->sslConfiguration(); +void ConnectRequest::onConnected(const QSharedPointer& pSocket) +{ + const auto& cfg = pSocket->sslConfiguration(); TlsChecker::logSslConfig(cfg, spawnMessageLogger(ifd)); bool isRemotePairing = false; @@ -111,7 +158,7 @@ return Env::getSingleton()->getMinimumIfdKeySize(pKeyAlgorithm); }; - if (mIfdDescriptor.isLocalIfd()) + if (mDiscovery.isLocalIfd()) { abortConnection |= !TlsChecker::hasValidEphemeralKeyLength(cfg.ephemeralServerKey(), minimalKeySizes); } @@ -128,93 +175,91 @@ const auto rootCert = TlsChecker::getRootCertificate(cfg.peerCertificateChain()); if (rootCert.isNull()) { - qCCritical(ifd) << "No root certificate found!"; + qCCritical(ifd) << " No root certificate found!"; abortConnection = true; } if (abortConnection) { - qCCritical(ifd) << "Server denied... abort connection!"; - mSocket->abort(); - Q_EMIT fireConnectionError(mIfdDescriptor, IfdErrorCode::REMOTE_HOST_REFUSED_CONNECTION); - return; - } - - if (mIfdDescriptor.isLocalIfd()) - { - qCDebug(ifd) << "Connected to localhost"; - Q_EMIT fireConnectionCreated(mIfdDescriptor, mSocket); + qCCritical(ifd) << " Server denied... abort connection!"; + pSocket->abort(); + mRemoteHostRefusedConnection = true; + processResult(pSocket); return; } - qCDebug(ifd) << "Connected to remote device"; - - auto& settings = Env::getSingleton()->getRemoteServiceSettings(); - if (isRemotePairing) + if (mDiscovery.isLocalIfd()) { - qCDebug(ifd) << "Pairing completed | Add certificate:" << rootCert; - settings.addTrustedCertificate(rootCert); + qCDebug(ifd) << " Connected to localhost"; } else { - auto info = settings.getRemoteInfo(rootCert); - info.setLastConnected(QDateTime::currentDateTime()); - settings.updateRemoteInfo(info); + qCDebug(ifd) << " Connected to remote device"; + + auto& settings = Env::getSingleton()->getRemoteServiceSettings(); + if (isRemotePairing) + { + qCDebug(ifd) << " Pairing completed | Add certificate:" << rootCert; + settings.addTrustedCertificate(rootCert); + } + else + { + auto info = settings.getRemoteInfo(rootCert); + info.setLastConnected(QDateTime::currentDateTime()); + settings.updateRemoteInfo(info); + } } - Q_EMIT fireConnectionCreated(mIfdDescriptor, mSocket); + processResult(pSocket); } -void ConnectRequest::onError(QAbstractSocket::SocketError pError) +void ConnectRequest::onError(const QSharedPointer& pSocket, QAbstractSocket::SocketError pError) { - if (mSocket) + if (pSocket) { - qCWarning(ifd) << "Connection error:" << pError << mSocket->errorString(); + qCWarning(ifd) << " Connection error:" << pError << pSocket->errorString(); } else { - qCWarning(ifd) << "Connection error:" << pError; + qCWarning(ifd) << " Connection error:" << pError; } - mTimer.stop(); if (pError == QAbstractSocket::SocketError::RemoteHostClosedError || pError == QAbstractSocket::SocketError::SslHandshakeFailedError) { - Q_EMIT fireConnectionError(mIfdDescriptor, IfdErrorCode::REMOTE_HOST_REFUSED_CONNECTION); - } - else - { - Q_EMIT fireConnectionError(mIfdDescriptor, IfdErrorCode::CONNECTION_ERROR); + mRemoteHostRefusedConnection = true; } + + processResult(pSocket); } void ConnectRequest::onTimeout() { - Q_EMIT fireConnectionTimeout(mIfdDescriptor); + processResult(); } void ConnectRequest::onPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator* pAuthenticator) const { - qCDebug(ifd) << "Request pairing..."; + qCDebug(ifd) << " Request pairing..."; pAuthenticator->setPreSharedKey(mPsk); } -void ConnectRequest::onSslErrors(const QList& pErrors) +void ConnectRequest::onSslErrors(const QSharedPointer& pSocket, const QList& pErrors) const { - QList allowedErrors = { + QList allowedErrors = { QSslError::HostNameMismatch }; - if (mIfdDescriptor.isLocalIfd()) + if (mDiscovery.isLocalIfd()) { allowedErrors << QSslError::NoPeerCertificate; } - const auto& config = mSocket->sslConfiguration(); + const auto& config = pSocket->sslConfiguration(); const auto& pairingCiphers = Env::getSingleton()->getTlsConfigRemoteIfd(SecureStorage::TlsSuite::PSK).getCiphers(); if (pairingCiphers.contains(config.sessionCipher())) { @@ -234,22 +279,62 @@ if (ignoreErrors) { - mSocket->ignoreSslErrors(pErrors); + pSocket->ignoreSslErrors(pErrors); return; } - qCDebug(ifd) << "Server is untrusted | cipher:" << config.sessionCipher() << "| certificate:" << config.peerCertificate() << "| error:" << pErrors; + qCDebug(ifd) << " Server is untrusted | cipher:" << config.sessionCipher() << "| certificate:" << config.peerCertificate() << "| error:" << pErrors; } -const IfdDescriptor& ConnectRequest::getIfdDescriptor() const +const Discovery& ConnectRequest::getDiscovery() const { - return mIfdDescriptor; + return mDiscovery; } void ConnectRequest::start() { - mSocket->open(mIfdDescriptor.getUrl()); + const auto& addresses = mDiscovery.getAddresses(); + if (addresses.isEmpty()) + { + Q_EMIT fireConnectionError(this, IfdErrorCode::INVALID_REQUEST); + return; + } + + if (mDiscovery.isLocalIfd()) + { + qCInfo(ifd) << "Start local connection. Addresses:" << addresses; + } + else + { + if (mPsk.isEmpty()) + { + qCInfo(ifd) << "Start reconnect to server. Addresses:" << addresses; + } + else + { + qCInfo(ifd) << "Start pairing to server. Addresses:" << addresses; + } + } + + const auto& tlsConfig = getTlsConfiguration(); + for (const auto& address : addresses) + { + const auto& socket = QSharedPointer(new QWebSocket(), &QObject::deleteLater); + socket->setProxy(QNetworkProxy(QNetworkProxy::NoProxy)); + socket->setSslConfiguration(tlsConfig); + + connect(socket.data(), &QWebSocket::preSharedKeyAuthenticationRequired, this, &ConnectRequest::onPreSharedKeyAuthenticationRequired); + connect(socket.data(), &QWebSocket::sslErrors, this, [socket, this](const QList& pErrors){onSslErrors(socket, pErrors);}); + connect(socket.data(), &QWebSocket::connected, this, [socket, this](){onConnected(socket);}); + connect(socket.data(), &QWebSocket::errorOccurred, this, [socket, this](QAbstractSocket::SocketError pError){onError(socket, pError);}); + + mSockets << socket; + QMetaObject::invokeMethod(this, [socket, address] { + socket->open(address); + }, Qt::QueuedConnection); + } + mTimer.start(); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/ConnectRequest.h ausweisapp2-2.4.0/src/ifd/base/ConnectRequest.h --- ausweisapp2-2.3.1/src/ifd/base/ConnectRequest.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/ConnectRequest.h 2025-10-30 10:10:48.000000000 +0000 @@ -6,9 +6,15 @@ #include "IfdConnector.h" +#include #include +#include #include + +class test_ConnectRequest; + + namespace governikus { @@ -17,36 +23,42 @@ { Q_OBJECT + friend class ::test_ConnectRequest; + private: - const IfdDescriptor mIfdDescriptor; + const Discovery mDiscovery; const QByteArray mPsk; - const QSharedPointer mSocket; + QList> mSockets; QTimer mTimer; + bool mRemoteHostRefusedConnection; - void setTlsConfiguration() const; + QSslConfiguration getTlsConfiguration() const; +#ifndef QT_NO_DEBUG + virtual +#endif + QAbstractSocket::SocketState getState(const QSharedPointer& pSocket) const; + void processResult(const QSharedPointer& pSocket = nullptr); private Q_SLOTS: - void onConnected(); - void onError(QAbstractSocket::SocketError pError); + void onConnected(const QSharedPointer& pSocket); + void onError(const QSharedPointer& pSocket, QAbstractSocket::SocketError pError); void onTimeout(); void onPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator* pAuthenticator) const; - void onSslErrors(const QList& pErrors); + void onSslErrors(const QSharedPointer& pSocket, const QList& pErrors) const; public: - ConnectRequest(const IfdDescriptor& pIfdDescriptor, + ConnectRequest(const Discovery& pDiscovery, const QByteArray& pPsk, int pTimeoutMs); ~ConnectRequest() override = default; - [[nodiscard]] const IfdDescriptor& getIfdDescriptor() const; + [[nodiscard]] const Discovery& getDiscovery() const; void start(); Q_SIGNALS: - void fireConnectionCreated(const IfdDescriptor& pIfdDescriptor, - const QSharedPointer& pWebSocket); - void fireConnectionError(const IfdDescriptor& pIfdDescriptor, const IfdErrorCode& pError); - void fireConnectionTimeout(const IfdDescriptor& pIfdDescriptor); + void fireConnectionCreated(ConnectRequest const* pRequest, const QSharedPointer& pWebSocket); + void fireConnectionError(ConnectRequest const* pRequest, const IfdErrorCode& pError); }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdCard.cpp ausweisapp2-2.4.0/src/ifd/base/IfdCard.cpp --- ausweisapp2-2.3.1/src/ifd/base/IfdCard.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdCard.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -15,11 +15,11 @@ #include "messages/IfdEstablishPaceChannelResponse.h" #include "messages/IfdModifyPin.h" #include "messages/IfdModifyPinResponse.h" +#include "messages/IfdStatus.h" #include "messages/IfdTransmit.h" #include "messages/IfdTransmitResponse.h" #include "pinpad/EstablishPaceChannel.h" #include "pinpad/PinModify.h" -#include "pinpad/PinModifyOutput.h" #include #include @@ -36,6 +36,7 @@ // mResponseAvailable is locked by the constructor, to revert the mutex behavior. // Locking this is a requirement for QWaitCondition. + mCardRemoved = false; mWaitingForAnswer = true; mExpectedAnswerType = pExpectedAnswer; @@ -68,7 +69,7 @@ } -void IfdCard::onMessageReceived(IfdMessageType pMessageTpe, const QJsonObject& pJsonObject) +void IfdCard::onMessageReceived(IfdMessageType pMessageType, const QJsonObject& pJsonObject) { QMutexLocker locker(&mProcessResponse); @@ -77,7 +78,7 @@ return; } - if (pMessageTpe == mExpectedAnswerType || pMessageTpe == IfdMessageType::IFDError) + if (pMessageType == mExpectedAnswerType || pMessageType == IfdMessageType::IFDError) { mResponse = pJsonObject; mWaitingForAnswer = false; @@ -85,7 +86,18 @@ return; } - qCWarning(card_remote) << "Ignoring unexpected message type:" << pMessageTpe; + if (pMessageType == IfdMessageType::IFDStatus) + { + const IfdStatus ifdStatus(pJsonObject); + if (!ifdStatus.getCardAvailable()) + { + qCDebug(card_remote) << "Card was removed while waiting for" << mExpectedAnswerType; + mCardRemoved = true; + return; + } + } + + qCWarning(card_remote) << "Ignoring unexpected message type:" << pMessageType; } @@ -117,6 +129,7 @@ , mReaderName(pReaderName) , mConnected(false) , mProgressMessage() + , mCardRemoved(false) { Q_ASSERT(mDispatcher); @@ -254,7 +267,12 @@ if (response.resultHasError()) { qCWarning(card_remote) << response.getResultMinor(); - return EstablishPaceChannelOutput(CardReturnCode::COMMAND_FAILED); + return EstablishPaceChannelOutput(response.getReturnCode()); + } + + if (mCardRemoved && response.getOutputData().getPaceReturnCode() == CardReturnCode::OK) + { + return EstablishPaceChannelOutput(CardReturnCode::CARD_NOT_FOUND); } return response.getOutputData(); @@ -302,12 +320,17 @@ return {CardReturnCode::COMMAND_FAILED}; } - const PinModifyOutput output(ResponseApdu(response.getOutputData())); + const ResponseApdu output(response.getOutputData()); if (response.resultHasError()) { qCWarning(card_remote) << response.getResultMinor(); - return {response.getReturnCode(), output.getResponseApdu()}; + return {response.getReturnCode(), output}; + } + + if (mCardRemoved && output.getStatusCode() == StatusCode::SUCCESS) + { + return {CardReturnCode::CARD_NOT_FOUND}; } - return {CardReturnCode::OK, output.getResponseApdu()}; + return {CardReturnCode::OK, output}; } diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdCard.h ausweisapp2-2.4.0/src/ifd/base/IfdCard.h --- ausweisapp2-2.3.1/src/ifd/base/IfdCard.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdCard.h 2025-10-30 10:10:48.000000000 +0000 @@ -40,11 +40,12 @@ QString mSlotHandle; bool mConnected; QString mProgressMessage; + bool mCardRemoved; bool sendMessage(const QSharedPointer& pMessage, IfdMessageType pExpectedAnswer, unsigned long pExtraTimeout = 0); private Q_SLOTS: - void onMessageReceived(IfdMessageType pMessageTpe, const QJsonObject& pJsonObject); + void onMessageReceived(IfdMessageType pMessageType, const QJsonObject& pJsonObject); void onDispatcherClosed(GlobalStatus::Code pCloseCode, const QByteArray& pId); Q_SIGNALS: diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdClientImpl.cpp ausweisapp2-2.4.0/src/ifd/base/IfdClientImpl.cpp --- ausweisapp2-2.3.1/src/ifd/base/IfdClientImpl.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdClientImpl.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -8,10 +8,15 @@ #include +#include + + using namespace governikus; + Q_DECLARE_LOGGING_CATEGORY(ifd) + IfdClientImpl::IfdClientImpl() : IfdClient() , mErrorCounter() @@ -62,36 +67,37 @@ } -QSharedPointer IfdClientImpl::mapToAndTakeConnectorPending(const IfdDescriptor& pIfdDescriptor) +QSharedPointer IfdClientImpl::mapToAndTakeConnectorPending(const QByteArray& pIfdId) { - QMutableListIterator> i(mIfdConnectorPending); - while (i.hasNext()) + const auto& iter = std::find_if(mIfdConnectorPending.begin(), mIfdConnectorPending.end(), + [&pIfdId](const auto& pEntry){ + return pEntry->containsIfdId(pIfdId); + }); + + if (iter == mIfdConnectorPending.end()) { - QSharedPointer entry = i.next(); - if (entry->containsEquivalent(pIfdDescriptor)) - { - i.remove(); - return entry; - } + Q_ASSERT(false); + return QSharedPointer(); } - Q_ASSERT(false); - return QSharedPointer(); + const auto entry = *iter; + mIfdConnectorPending.erase(iter); + return entry; } -void IfdClientImpl::onDispatcherCreated(const IfdDescriptor& pIfdDescriptor, const QSharedPointer& pDispatcher) +void IfdClientImpl::onDispatcherCreated(const QByteArray& pIfdId, const QSharedPointer& pDispatcher) { - mErrorCounter[pIfdDescriptor.getIfdId()] = 0; - const QSharedPointer& entry = mapToAndTakeConnectorPending(pIfdDescriptor); + mErrorCounter[pIfdId] = 0; + const QSharedPointer& entry = mapToAndTakeConnectorPending(pIfdId); if (entry.isNull()) { - qCDebug(ifd) << "Failed to map IfdConnector response:" << pIfdDescriptor; + qCDebug(ifd) << "Failed to map IfdConnector response:" << pIfdId; Q_ASSERT(false); return; } - mConnectedDeviceIds.append(entry->getIfdDescriptor().getIfdId()); + mConnectedDeviceIds.append(entry->getDiscovery().getIfdId()); connect(pDispatcher.data(), &IfdDispatcherClient::fireClosed, this, &IfdClientImpl::onDispatcherDestroyed); Q_EMIT fireEstablishConnectionDone(entry, GlobalStatus::Code::No_Error); @@ -99,27 +105,26 @@ } -void IfdClientImpl::onDispatcherError(const IfdDescriptor& pIfdDescriptor, IfdErrorCode pErrorCode) +void IfdClientImpl::onDispatcherError(const QByteArray& pIfdId, IfdErrorCode pErrorCode) { - const QSharedPointer& entry = mapToAndTakeConnectorPending(pIfdDescriptor); + const QSharedPointer& entry = mapToAndTakeConnectorPending(pIfdId); if (entry.isNull()) { - qCDebug(ifd) << "Failed to map IfdConnector response:" << pIfdDescriptor; + qCDebug(ifd) << "Failed to map IfdConnector response:" << pIfdId; Q_ASSERT(false); return; } - const auto& ifdId = pIfdDescriptor.getIfdId(); if (pErrorCode == IfdErrorCode::REMOTE_HOST_REFUSED_CONNECTION || pErrorCode == IfdErrorCode::NO_SUPPORTED_API_LEVEL) { - mErrorCounter[ifdId] += 1; - if (mErrorCounter.value(ifdId) >= 7) + mErrorCounter[pIfdId] += 1; + if (mErrorCounter.value(pIfdId) >= 7) { - qCCritical(ifd) << "Remote device refused connection seven times, removing certificate with fingerprint:" << ifdId; + qCCritical(ifd) << "Remote device refused connection seven times, removing certificate with fingerprint:" << pIfdId; RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); - const QString& deviceNameEscaped = settings.getRemoteInfo(ifdId).getNameEscaped(); - settings.removeTrustedCertificate(ifdId); - mErrorCounter[ifdId] = 0; + const QString& deviceNameEscaped = settings.getRemoteInfo(pIfdId).getNameEscaped(); + settings.removeTrustedCertificate(pIfdId); + mErrorCounter[pIfdId] = 0; if (!deviceNameEscaped.isEmpty()) { Q_EMIT fireCertificateRemoved(deviceNameEscaped); @@ -128,7 +133,7 @@ } else { - mErrorCounter[ifdId] = 0; + mErrorCounter[pIfdId] = 0; } Q_EMIT fireEstablishConnectionDone(entry, IfdConnector::errorToGlobalStatus(pErrorCode)); @@ -155,7 +160,7 @@ qCDebug(ifd) << "Establishing connection to remote device."; const auto& localCopy = mIfdConnector; QMetaObject::invokeMethod(localCopy.data(), [localCopy, pEntry, pPsk] { - localCopy->onConnectRequest(pEntry->getIfdDescriptor(), pPsk); + localCopy->onConnectRequest(pEntry->getDiscovery(), pPsk); }, Qt::QueuedConnection); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdClientImpl.h ausweisapp2-2.4.0/src/ifd/base/IfdClientImpl.h --- ausweisapp2-2.3.1/src/ifd/base/IfdClientImpl.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdClientImpl.h 2025-10-30 10:10:48.000000000 +0000 @@ -28,11 +28,11 @@ void bootstrapConnectorThread(); void shutdownConnectorThread(); - QSharedPointer mapToAndTakeConnectorPending(const IfdDescriptor& pIfdDescriptor); + QSharedPointer mapToAndTakeConnectorPending(const QByteArray& pIfdId); protected Q_SLOTS: - void onDispatcherCreated(const IfdDescriptor& pIfdDescriptor, const QSharedPointer& pDispatcher); - void onDispatcherError(const IfdDescriptor& pIfdDescriptor, IfdErrorCode pErrorCode); + void onDispatcherCreated(const QByteArray& pIfdId, const QSharedPointer& pDispatcher); + void onDispatcherError(const QByteArray& pIfdId, IfdErrorCode pErrorCode); void onDispatcherDestroyed(GlobalStatus::Code pCloseCode, const QByteArray& pId); public: diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdConnector.cpp ausweisapp2-2.4.0/src/ifd/base/IfdConnector.cpp --- ausweisapp2-2.3.1/src/ifd/base/IfdConnector.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdConnector.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -8,6 +8,7 @@ #include + using namespace governikus; @@ -28,9 +29,6 @@ case IfdErrorCode::NO_SUPPORTED_API_LEVEL: return GlobalStatus::Code::IfdConnector_NoSupportedApiLevel; - case IfdErrorCode::CONNECTION_TIMEOUT: - return GlobalStatus::Code::IfdConnector_ConnectionTimeout; - case IfdErrorCode::CONNECTION_ERROR: return GlobalStatus::Code::IfdConnector_ConnectionError; diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdConnector.h ausweisapp2-2.4.0/src/ifd/base/IfdConnector.h --- ausweisapp2-2.3.1/src/ifd/base/IfdConnector.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdConnector.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,17 +5,17 @@ #pragma once #include "EnumHelper.h" -#include "IfdDescriptor.h" #include "IfdDispatcherClient.h" +#include "messages/Discovery.h" #include + namespace governikus { defineEnumType(IfdErrorCode, INVALID_REQUEST, NO_SUPPORTED_API_LEVEL, - CONNECTION_TIMEOUT, CONNECTION_ERROR, REMOTE_HOST_REFUSED_CONNECTION) @@ -32,11 +32,11 @@ static GlobalStatus errorToGlobalStatus(IfdErrorCode pErrorCode); public Q_SLOTS: - virtual void onConnectRequest(const IfdDescriptor& pIfdDescriptor, const QByteArray& pPsk) = 0; + virtual void onConnectRequest(const Discovery& pDiscovery, const QByteArray& pPsk) = 0; Q_SIGNALS: - void fireDispatcherCreated(const IfdDescriptor& pIfdDescriptor, const QSharedPointer& pClientDispatcher); - void fireDispatcherError(const IfdDescriptor& pIfdDescriptor, IfdErrorCode pErrorCode); + void fireDispatcherCreated(const QByteArray& pIfdId, const QSharedPointer& pClientDispatcher); + void fireDispatcherError(const QByteArray& pIfdId, IfdErrorCode pErrorCode); }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdConnectorImpl.cpp ausweisapp2-2.4.0/src/ifd/base/IfdConnectorImpl.cpp --- ausweisapp2-2.3.1/src/ifd/base/IfdConnectorImpl.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdConnectorImpl.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -8,7 +8,8 @@ #include "WebSocketChannel.h" #include -#include + +#include Q_DECLARE_LOGGING_CATEGORY(ifd) @@ -28,53 +29,49 @@ using namespace governikus; -void IfdConnectorImpl::removeRequest(const IfdDescriptor& pIfdDescriptor) +QSharedPointer IfdConnectorImpl::removeRequest(ConnectRequest const* pRequest) { - QMutableListIterator> requestIterator(mPendingRequests); - while (requestIterator.hasNext()) + const auto& iter = std::find_if(mPendingRequests.begin(), mPendingRequests.end(), + [&pRequest](const auto& pCurrentRequest){ + return pCurrentRequest == pRequest; + }); + + if (iter == mPendingRequests.end()) { - const QSharedPointer item = requestIterator.next(); - Q_ASSERT(item); - if (item.isNull()) - { - qCCritical(ifd) << "Unexpected null pending request"; - - requestIterator.remove(); - } - else if (item->getIfdDescriptor() == pIfdDescriptor) - { - requestIterator.remove(); - } + return QSharedPointer(); } -} - - -void IfdConnectorImpl::onConnectionCreated(const IfdDescriptor& pIfdDescriptor, - const QSharedPointer& pWebSocket) -{ - const QSharedPointer channel(new WebSocketChannel(pWebSocket), &QObject::deleteLater); - const IfdVersion::Version latestSupportedVersion = IfdVersion::selectLatestSupported(pIfdDescriptor.getApiVersions()); - const QSharedPointer dispatcher(Env::create(latestSupportedVersion, channel), &QObject::deleteLater); - - removeRequest(pIfdDescriptor); - Q_EMIT fireDispatcherCreated(pIfdDescriptor, dispatcher); + const auto request = *iter; + mPendingRequests.erase(iter); + return request; } -void IfdConnectorImpl::onConnectionError(const IfdDescriptor& pIfdDescriptor, const IfdErrorCode& pError) +void IfdConnectorImpl::onConnectionCreated(ConnectRequest const* pRequest, const QSharedPointer& pWebSocket) { - removeRequest(pIfdDescriptor); + const auto& connectRequest = removeRequest(pRequest); + if (!connectRequest) + { + return; + } - Q_EMIT fireDispatcherError(pIfdDescriptor, pError); + const auto& discovery = connectRequest->getDiscovery(); + const QSharedPointer channel(new WebSocketChannel(pWebSocket), &QObject::deleteLater); + const IfdVersion::Version latestSupportedVersion = IfdVersion::selectLatestSupported(discovery.getSupportedApis()); + const QSharedPointer dispatcher(Env::create(latestSupportedVersion, channel), &QObject::deleteLater); + Q_EMIT fireDispatcherCreated(discovery.getIfdId(), dispatcher); } -void IfdConnectorImpl::onConnectionTimeout(const IfdDescriptor& pIfdDescriptor) +void IfdConnectorImpl::onConnectionError(ConnectRequest const* pRequest, const IfdErrorCode& pError) { - removeRequest(pIfdDescriptor); + const auto& connectRequest = removeRequest(pRequest); + if (!connectRequest) + { + return; + } - Q_EMIT fireDispatcherError(pIfdDescriptor, IfdErrorCode::CONNECTION_TIMEOUT); + Q_EMIT fireDispatcherError(connectRequest->getDiscovery().getIfdId(), pError); } @@ -85,25 +82,24 @@ } -void IfdConnectorImpl::onConnectRequest(const IfdDescriptor& pIfdDescriptor, const QByteArray& pPsk) +void IfdConnectorImpl::onConnectRequest(const Discovery& pDiscovery, const QByteArray& pPsk) { - if (pIfdDescriptor.isNull() || pIfdDescriptor.getIfdName().isEmpty()) + if (pDiscovery.getIfdName().isEmpty() || pDiscovery.addressesMissing()) { - Q_EMIT fireDispatcherError(pIfdDescriptor, IfdErrorCode::INVALID_REQUEST); + Q_EMIT fireDispatcherError(pDiscovery.getIfdId(), IfdErrorCode::INVALID_REQUEST); return; } - if (!pIfdDescriptor.isSupported()) + if (!pDiscovery.isSupported()) { - Q_EMIT fireDispatcherError(pIfdDescriptor, IfdErrorCode::NO_SUPPORTED_API_LEVEL); + Q_EMIT fireDispatcherError(pDiscovery.getIfdId(), IfdErrorCode::NO_SUPPORTED_API_LEVEL); return; } - const QSharedPointer newRequest(new ConnectRequest(pIfdDescriptor, pPsk, mConnectTimeoutMs), &QObject::deleteLater); + const QSharedPointer newRequest(new ConnectRequest(pDiscovery, pPsk, mConnectTimeoutMs), &QObject::deleteLater); mPendingRequests += newRequest; connect(newRequest.data(), &ConnectRequest::fireConnectionCreated, this, &IfdConnectorImpl::onConnectionCreated); connect(newRequest.data(), &ConnectRequest::fireConnectionError, this, &IfdConnectorImpl::onConnectionError); - connect(newRequest.data(), &ConnectRequest::fireConnectionTimeout, this, &IfdConnectorImpl::onConnectionTimeout); qCDebug(ifd) << "Request connection."; newRequest->start(); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdConnectorImpl.h ausweisapp2-2.4.0/src/ifd/base/IfdConnectorImpl.h --- ausweisapp2-2.3.1/src/ifd/base/IfdConnectorImpl.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdConnectorImpl.h 2025-10-30 10:10:48.000000000 +0000 @@ -10,6 +10,10 @@ #include #include + +class test_IfdConnector; + + namespace governikus { @@ -17,23 +21,23 @@ : public IfdConnector { Q_OBJECT + friend class ::test_IfdConnector; private: const int mConnectTimeoutMs; QList> mPendingRequests; - void removeRequest(const IfdDescriptor& pIfdDescriptor); + QSharedPointer removeRequest(ConnectRequest const* pRequest); private Q_SLOTS: - void onConnectionCreated(const IfdDescriptor& pIfdDescriptor, const QSharedPointer& pWebSocket); - void onConnectionError(const IfdDescriptor& pIfdDescriptor, const IfdErrorCode& pError); - void onConnectionTimeout(const IfdDescriptor& pIfdDescriptor); + void onConnectionCreated(ConnectRequest const* pRequest, const QSharedPointer& pWebSocket); + void onConnectionError(ConnectRequest const* pRequest, const IfdErrorCode& pError); public: explicit IfdConnectorImpl(int pConnectTimeoutMs = 5000); ~IfdConnectorImpl() override = default; - void onConnectRequest(const IfdDescriptor& pIfdDescriptor, const QByteArray& pPsk) override; + void onConnectRequest(const Discovery& pDiscovery, const QByteArray& pPsk) override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdDescriptor.cpp ausweisapp2-2.4.0/src/ifd/base/IfdDescriptor.cpp --- ausweisapp2-2.3.1/src/ifd/base/IfdDescriptor.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdDescriptor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -/** - * Copyright (c) 2017-2025 Governikus GmbH & Co. KG, Germany - */ - -#include "IfdDescriptor.h" - -#include "Initializer.h" - -using namespace governikus; - -INIT_FUNCTION([] { - qRegisterMetaType("IfdDescriptor"); - }) - -namespace -{ - -QUrl urlFromMsgAndHost(const Discovery& pDiscovery, - const QHostAddress& pHostAddress) -{ - if (pDiscovery.isIncomplete()) - { - return QUrl(); - } - - QUrl url; - url.setScheme(QStringLiteral("wss")); - url.setHost(pHostAddress.toString()); - url.setPort(pDiscovery.getPort()); - - return url; -} - - -} // namespace - - -IfdDescriptor::IfdDescriptorData::IfdDescriptorData(const QString& pIfdName, - const QByteArray& pIfdId, - const QList& pApiVersions, - const bool pIsPairingAnnounced, - const QUrl& pRemoteUrl, - bool pIsLocalIfd) - : mIfdName(pIfdName) - , mIfdId(pIfdId) - , mApiVersions(pApiVersions) - , mIsPairingAnnounced(pIsPairingAnnounced) - , mUrl(pRemoteUrl) - , mIsLocalIfd(pIsLocalIfd) -{ -} - - -IfdDescriptor::IfdDescriptorData::~IfdDescriptorData() = default; - - -bool IfdDescriptor::IfdDescriptorData::operator==(const IfdDescriptorData& pOther) const -{ - return mIfdName == pOther.mIfdName && - mIfdId == pOther.mIfdId && - mApiVersions == pOther.mApiVersions && - mIsPairingAnnounced == pOther.mIsPairingAnnounced && - mUrl == pOther.mUrl; -} - - -bool IfdDescriptor::IfdDescriptorData::isSameIfd(const IfdDescriptorData& pOther) const -{ - return mIfdId == pOther.mIfdId; -} - - -IfdDescriptor::IfdDescriptor(const Discovery& pDiscovery, const QHostAddress& pHostAddress, bool pLocalIfd) - : d() -{ - const QUrl url = urlFromMsgAndHost(pDiscovery, pHostAddress); - if (url.isEmpty() || url.host().isEmpty()) - { - return; - } - - const QString& ifdName = pDiscovery.getIfdName(); - const QByteArray& ifdId = pDiscovery.getIfdId(); - const QList& supportedApis = pDiscovery.getSupportedApis(); - const bool isPairing = pDiscovery.getPairing(); - d = new IfdDescriptorData(ifdName, ifdId, supportedApis, isPairing, url, pLocalIfd); -} - - -const QString& IfdDescriptor::getIfdName() const -{ - static const QString EMPTY_STRING; - - return d.data() == nullptr ? EMPTY_STRING : d->mIfdName; -} - - -const QByteArray& IfdDescriptor::getIfdId() const -{ - static const QByteArray EMPTY_ARRAY; - - return d.data() == nullptr ? EMPTY_ARRAY : d->mIfdId; -} - - -const QList& IfdDescriptor::getApiVersions() const -{ - static const QList EMPTY_VECTOR; - - return d.data() == nullptr ? EMPTY_VECTOR : d->mApiVersions; -} - - -bool IfdDescriptor::isSupported() const -{ - return IfdVersion(IfdVersion::selectLatestSupported(getApiVersions())).isValid(); -} - - -bool IfdDescriptor::isPairingAnnounced() const -{ - return !isNull() && d->mIsPairingAnnounced; -} - - -const QUrl& IfdDescriptor::getUrl() const -{ - static const QUrl EMPTY_URL; - - return d.data() == nullptr ? EMPTY_URL : d->mUrl; -} - - -bool IfdDescriptor::isNull() const -{ - return d.data() == nullptr; -} - - -bool IfdDescriptor::isLocalIfd() const -{ - return !isNull() && d->mIsLocalIfd; -} - - -bool IfdDescriptor::operator==(const IfdDescriptor& pOther) const -{ - return this == &pOther || - (d.data() == nullptr && pOther.d.data() == nullptr) || - (d.data() != nullptr && pOther.d.data() != nullptr && *d == *(pOther.d)); -} - - -bool IfdDescriptor::isSameIfd(const IfdDescriptor& pOther) const -{ - return this == &pOther || - (d.data() == nullptr && pOther.d.data() == nullptr) || - (d.data() != nullptr && pOther.d.data() != nullptr && d->isSameIfd(*(pOther.d))); -} diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdDescriptor.h ausweisapp2-2.4.0/src/ifd/base/IfdDescriptor.h --- ausweisapp2-2.3.1/src/ifd/base/IfdDescriptor.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdDescriptor.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -/** - * Copyright (c) 2017-2025 Governikus GmbH & Co. KG, Germany - */ - -#pragma once - -#include "messages/Discovery.h" - -#include -#include -#include -#include - - -namespace governikus -{ - -class IfdDescriptor -{ - private: - class IfdDescriptorData - : public QSharedData - { - public: - IfdDescriptorData(const QString& pIfdName, - const QByteArray& pIfdId, - const QList& pApiVersions, - const bool pIsPairingAnnounced, - const QUrl& pUrl, - bool pIsLocalIfd); - - virtual ~IfdDescriptorData(); - - const QString mIfdName; - const QByteArray mIfdId; - const QList mApiVersions; - const bool mIsPairingAnnounced; - const QUrl mUrl; - const bool mIsLocalIfd; - - bool operator==(const IfdDescriptorData& pOther) const; - bool isSameIfd(const IfdDescriptorData& pOther) const; - }; - - QSharedDataPointer d; - - public: - IfdDescriptor() = default; - IfdDescriptor(const Discovery& pDiscovery, const QHostAddress& pHostAddress, bool pLocalIfd = false); - - ~IfdDescriptor() = default; - - [[nodiscard]] const QString& getIfdName() const; - [[nodiscard]] const QByteArray& getIfdId() const; - [[nodiscard]] const QList& getApiVersions() const; - [[nodiscard]] bool isSupported() const; - [[nodiscard]] bool isPairingAnnounced() const; - [[nodiscard]] const QUrl& getUrl() const; - [[nodiscard]] bool isNull() const; - [[nodiscard]] bool isLocalIfd() const; - - bool operator==(const IfdDescriptor& pOther) const; - [[nodiscard]] bool isSameIfd(const IfdDescriptor& pOther) const; - -}; - - -inline QDebug operator<<(QDebug pDbg, const IfdDescriptor& pIfdDescriptor) -{ - QDebugStateSaver saver(pDbg); - return pDbg.noquote().nospace() << "IFD(" << pIfdDescriptor.getIfdName() << ", " << - pIfdDescriptor.getIfdId() << ", " << - pIfdDescriptor.getUrl() << ", " << - pIfdDescriptor.getApiVersions() << ")"; -} - - -} // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdDispatcherClient.cpp ausweisapp2-2.4.0/src/ifd/base/IfdDispatcherClient.cpp --- ausweisapp2-2.3.1/src/ifd/base/IfdDispatcherClient.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdDispatcherClient.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -19,7 +19,7 @@ { template<> IfdDispatcherClient* createNewObject&>(IfdVersion::Version&& pVersion, const QSharedPointer& pChannel) { - return new IfdDispatcherClient(pVersion, pChannel); + return new IfdDispatcherClient(std::move(pVersion), pChannel); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdList.h ausweisapp2-2.4.0/src/ifd/base/IfdList.h --- ausweisapp2-2.3.1/src/ifd/base/IfdList.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdList.h 2025-10-30 10:10:48.000000000 +0000 @@ -4,8 +4,8 @@ #pragma once -#include "IfdDescriptor.h" #include "IfdListEntry.h" +#include "messages/Discovery.h" #include @@ -27,7 +27,7 @@ IfdList() = default; ~IfdList() override = default; - virtual void update(const IfdDescriptor& pDescriptor) = 0; + virtual void update(const Discovery& pDiscovery) = 0; virtual void clear() = 0; [[nodiscard]] virtual QList> getIfdList() const; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdListEntry.cpp ausweisapp2-2.4.0/src/ifd/base/IfdListEntry.cpp --- ausweisapp2-2.3.1/src/ifd/base/IfdListEntry.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdListEntry.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -17,8 +17,8 @@ }) -IfdListEntry::IfdListEntry(const IfdDescriptor& pIfdDescriptor) - : mIfdDescriptor(pIfdDescriptor) +IfdListEntry::IfdListEntry(const Discovery& pDiscovery) + : mDiscovery(pDiscovery) , mLastSeen(QTime::currentTime()) , mLastSeenHistory() { @@ -38,21 +38,14 @@ bool IfdListEntry::cleanUpSeenTimestamps(int pReaderResponsiveTimeout) { - bool entryRemoved = false; const auto visibilityOld = getPercentSeen(); - const QTime threshold(QTime::currentTime().addMSecs(-pReaderResponsiveTimeout)); - QMutableListIterator i(mLastSeenHistory); - while (i.hasNext()) - { - if (i.next() < threshold) - { - i.remove(); - entryRemoved = true; - } - } - return entryRemoved && getPercentSeen() != visibilityOld; + const auto removed = erase_if(mLastSeenHistory, [&threshold](const auto pValue){ + return pValue < threshold; + }); + + return removed > 0 && getPercentSeen() != visibilityOld; } @@ -68,23 +61,15 @@ } -void IfdListEntry::setIfdDescriptor(const IfdDescriptor& pIfdDescriptor) -{ - mIfdDescriptor = pIfdDescriptor; -} - - -bool IfdListEntry::containsEquivalent(const IfdDescriptor& pIfdDescriptor) const +void IfdListEntry::setDiscovery(const Discovery& pDiscovery) { - return mIfdDescriptor.isSameIfd(pIfdDescriptor); + mDiscovery = pDiscovery; } -bool IfdListEntry::isEqual(const IfdListEntry* const pOther) const +bool IfdListEntry::containsIfdId(const QByteArray& pIfdId) const { - return pOther != nullptr && - mIfdDescriptor == pOther->mIfdDescriptor && - mLastSeen == pOther->mLastSeen; + return mDiscovery.getIfdId() == pIfdId; } @@ -94,7 +79,7 @@ } -const IfdDescriptor& IfdListEntry::getIfdDescriptor() const +const Discovery& IfdListEntry::getDiscovery() const { - return mIfdDescriptor; + return mDiscovery; } diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdListEntry.h ausweisapp2-2.4.0/src/ifd/base/IfdListEntry.h --- ausweisapp2-2.3.1/src/ifd/base/IfdListEntry.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdListEntry.h 2025-10-30 10:10:48.000000000 +0000 @@ -4,7 +4,7 @@ #pragma once -#include "IfdDescriptor.h" +#include "messages/Discovery.h" #include #include @@ -18,24 +18,23 @@ Q_DISABLE_COPY(IfdListEntry) private: - IfdDescriptor mIfdDescriptor; + Discovery mDiscovery; QTime mLastSeen; QList mLastSeenHistory; public: - explicit IfdListEntry(const IfdDescriptor& pIfdDescriptor); + explicit IfdListEntry(const Discovery& pDiscovery); void setLastSeenToNow(); bool cleanUpSeenTimestamps(int pReaderResponsiveTimeout); [[nodiscard]] int getPercentSeen(int pCheckInterval = 1000, int pTimeFrame = 5000) const; - void setIfdDescriptor(const IfdDescriptor& pIfdDescriptor); + void setDiscovery(const Discovery& pDiscovery); - [[nodiscard]] bool containsEquivalent(const IfdDescriptor& pIfdDescriptor) const; - bool isEqual(const IfdListEntry* const pOther) const; + [[nodiscard]] bool containsIfdId(const QByteArray& pIfdId) const; [[nodiscard]] const QTime& getLastSeen() const; - [[nodiscard]] const IfdDescriptor& getIfdDescriptor() const; + [[nodiscard]] const Discovery& getDiscovery() const; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdListImpl.cpp ausweisapp2-2.4.0/src/ifd/base/IfdListImpl.cpp --- ausweisapp2-2.3.1/src/ifd/base/IfdListImpl.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdListImpl.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -26,21 +26,21 @@ } -void IfdListImpl::update(const IfdDescriptor& pDescriptor) +void IfdListImpl::update(const Discovery& pDiscovery) { for (const QSharedPointer& entry : std::as_const(mResponsiveList)) { - if (entry->containsEquivalent(pDescriptor)) + if (entry->containsIfdId(pDiscovery.getIfdId())) { entry->setLastSeenToNow(); - entry->setIfdDescriptor(pDescriptor); + entry->setDiscovery(pDiscovery); Q_EMIT fireDeviceUpdated(entry); return; } } - const auto& newDevice = QSharedPointer::create(pDescriptor); + const auto& newDevice = QSharedPointer::create(pDiscovery); mResponsiveList += newDevice; if (!mTimer.isActive()) @@ -72,22 +72,19 @@ void IfdListImpl::onProcessUnresponsiveRemoteReaders() { const QTime threshold(QTime::currentTime().addMSecs(-mReaderResponsiveTimeout)); - QMutableListIterator i(mResponsiveList); - while (i.hasNext()) - { - const QSharedPointer entry = i.next(); - if (entry->getLastSeen() < threshold) - { - i.remove(); - Q_EMIT fireDeviceVanished(entry); - continue; - } - if (entry->cleanUpSeenTimestamps(mReaderResponsiveTimeout)) - { - Q_EMIT fireDeviceUpdated(entry); - } - } + erase_if(mResponsiveList, [this, &threshold](const auto& pEntry) { + if (pEntry->getLastSeen() < threshold) + { + Q_EMIT fireDeviceVanished(pEntry); + return true; + } + if (pEntry->cleanUpSeenTimestamps(mReaderResponsiveTimeout)) + { + Q_EMIT fireDeviceUpdated(pEntry); + } + return false; + }); if (mResponsiveList.isEmpty()) { diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdListImpl.h ausweisapp2-2.4.0/src/ifd/base/IfdListImpl.h --- ausweisapp2-2.3.1/src/ifd/base/IfdListImpl.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdListImpl.h 2025-10-30 10:10:48.000000000 +0000 @@ -26,10 +26,10 @@ void onProcessUnresponsiveRemoteReaders(); public: - IfdListImpl(int pCheckInterval = 1000, int pReaderResponsiveTimeout = 5000); + explicit IfdListImpl(int pCheckInterval = 1000, int pReaderResponsiveTimeout = 5000); ~IfdListImpl() override; - void update(const IfdDescriptor& pDescriptor) override; + void update(const Discovery& pDiscovery) override; void clear() override; [[nodiscard]] QList> getIfdList() const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdReaderManagerPlugin.cpp ausweisapp2-2.4.0/src/ifd/base/IfdReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/src/ifd/base/IfdReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,14 +5,15 @@ #include "IfdReaderManagerPlugin.h" #include "AppSettings.h" -#include "IfdReader.h" #include "messages/IfdError.h" #include "messages/IfdGetStatus.h" #include + using namespace governikus; + Q_DECLARE_LOGGING_CATEGORY(card_remote) @@ -26,7 +27,7 @@ continue; } - if (QScopedPointer reader(mReaderList.take(readerName)); reader) + if (QSharedPointer reader(mReaders.take(readerName)); reader) { Q_EMIT fireReaderRemoved(reader->getReaderInfo()); } @@ -58,12 +59,12 @@ void IfdReaderManagerPlugin::processConnectedReader(const QString& pReaderName, const IfdStatus& pIfdStatus, const QSharedPointer& pDispatcher, const QByteArray& pId) { bool newReader = false; - if (mReaderList.contains(pReaderName)) + if (mReaders.contains(pReaderName)) { - if (auto* reader = mReaderList.value(pReaderName); reader) + if (const auto& reader = mReaders.value(pReaderName); reader) { qCDebug(card_remote) << "Update reader" << pReaderName; - static_cast(reader)->updateStatus(pIfdStatus); + reader->updateStatus(pIfdStatus); return; } qCDebug(card_remote) << "Enable reader" << pReaderName; @@ -74,13 +75,13 @@ newReader = true; } - auto* reader = new IfdReader(getInfo().getPluginType(), pReaderName, pDispatcher, pIfdStatus); - connect(reader, &IfdReader::fireCardInserted, this, &IfdReaderManagerPlugin::fireCardInserted); - connect(reader, &IfdReader::fireCardRemoved, this, &IfdReaderManagerPlugin::fireCardRemoved); - connect(reader, &IfdReader::fireCardInfoChanged, this, &IfdReaderManagerPlugin::fireCardInfoChanged); - connect(reader, &IfdReader::fireReaderPropertiesUpdated, this, &IfdReaderManagerPlugin::fireReaderPropertiesUpdated); + const auto& reader = QSharedPointer::create(getInfo().getPluginType(), pReaderName, pDispatcher, pIfdStatus); + connect(reader.data(), &IfdReader::fireCardInserted, this, &IfdReaderManagerPlugin::fireCardInserted); + connect(reader.data(), &IfdReader::fireCardRemoved, this, &IfdReaderManagerPlugin::fireCardRemoved); + connect(reader.data(), &IfdReader::fireCardInfoChanged, this, &IfdReaderManagerPlugin::fireCardInfoChanged); + connect(reader.data(), &IfdReader::fireReaderPropertiesUpdated, this, &IfdReaderManagerPlugin::fireReaderPropertiesUpdated); - mReaderList.insert(pReaderName, reader); + mReaders.insert(pReaderName, reader); if (newReader) { mReadersForDispatcher.insert(pId, pReaderName); @@ -118,19 +119,19 @@ ReaderInfo readerInfo(readerName); readerInfo.setBasicReader(!ifdStatus.hasPinPad()); readerInfo.setMaxApduLength(ifdStatus.getMaxApduLength()); - if (mReaderList.contains(readerName)) + if (mReaders.contains(readerName)) { qCDebug(card_remote) << "Disable reader" << readerName; - if (QScopedPointer reader(mReaderList.take(readerName)); reader) + if (const auto& reader = mReaders.take(readerName); reader) { - mReaderList.insert(readerName, nullptr); + mReaders.insert(readerName, nullptr); } Q_EMIT fireReaderPropertiesUpdated(readerInfo); return; } qCDebug(card_remote) << "Advertise reader" << readerName; - mReaderList.insert(readerName, nullptr); + mReaders.insert(readerName, nullptr); mReadersForDispatcher.insert(pId, readerName); Q_EMIT fireReaderAdded(readerInfo); } @@ -214,7 +215,7 @@ : ReaderManagerPlugin(pPluginType, pAvailable, pPluginEnabled) , mReadersForDispatcher() , mDispatcherList() - , mReaderList() + , mReaders() { } @@ -222,7 +223,7 @@ IfdReaderManagerPlugin::~IfdReaderManagerPlugin() { // can't wait for removeAllDispatchers answer because were are in dtor - // and must delete Reader* of mReaderList. + // and must delete Reader* of mReaders. const auto list = mDispatcherList.keys(); for (const auto& id : list) { @@ -240,11 +241,9 @@ } -QList IfdReaderManagerPlugin::getReaders() const +QPointer IfdReaderManagerPlugin::getReader(const QString& pReaderName) const { - auto readerList = mReaderList.values(); - readerList.removeAll(nullptr); - return readerList; + return mReaders.value(pReaderName).data(); } @@ -264,7 +263,7 @@ void IfdReaderManagerPlugin::insert(const QString& pReaderName, const QVariant& pData) { - Reader* const reader = mReaderList.value(pReaderName); + const auto& reader = mReaders.value(pReaderName); if (!reader || !reader->getReaderInfo().isInsertable()) { qCDebug(card_remote) << "Skipping insert because reader is not connected or there is no card available"; @@ -296,3 +295,12 @@ { return mDispatcherList; } + + +void IfdReaderManagerPlugin::shelveAll() const +{ + for (const auto& reader : mReaders) + { + shelve(reader.data()); + } +} diff -Nru ausweisapp2-2.3.1/src/ifd/base/IfdReaderManagerPlugin.h ausweisapp2-2.4.0/src/ifd/base/IfdReaderManagerPlugin.h --- ausweisapp2-2.3.1/src/ifd/base/IfdReaderManagerPlugin.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/IfdReaderManagerPlugin.h 2025-10-30 10:10:48.000000000 +0000 @@ -6,6 +6,7 @@ #include "IfdClient.h" #include "IfdDispatcherClient.h" +#include "IfdReader.h" #include "ReaderManagerPlugin.h" #include "messages/IfdStatus.h" @@ -29,7 +30,7 @@ private: QMultiMap mReadersForDispatcher; QMap> mDispatcherList; - QMap mReaderList; + QMap> mReaders; void processConnectedReader(const QString& pReaderName, const IfdStatus& pIfdStatus, const QSharedPointer& pDispatcher, const QByteArray& pId); void handleIFDStatus(const QJsonObject& pJsonObject, const QByteArray& pId); @@ -48,17 +49,19 @@ virtual IfdClient* getIfdClient() const = 0; public: - IfdReaderManagerPlugin(ReaderManagerPluginType pPluginType, bool pAvailable = false, bool pPluginEnabled = false); + explicit IfdReaderManagerPlugin(ReaderManagerPluginType pPluginType, bool pAvailable = false, bool pPluginEnabled = false); ~IfdReaderManagerPlugin() override; void init() override; - [[nodiscard]] QList getReaders() const override; + + [[nodiscard]] QPointer getReader(const QString& pReaderName) const override; void insert(const QString& pReaderName, const QVariant& pData) override; void startScan(bool pAutoConnect) override; void stopScan(const QString& pError = QString()) override; + void shelveAll() const override; }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ifd/base/ServerMessageHandlerImpl.cpp ausweisapp2-2.4.0/src/ifd/base/ServerMessageHandlerImpl.cpp --- ausweisapp2-2.3.1/src/ifd/base/ServerMessageHandlerImpl.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/ServerMessageHandlerImpl.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -23,7 +23,6 @@ #include "messages/IfdStatus.h" #include "messages/IfdTransmit.h" #include "messages/IfdTransmitResponse.h" -#include "pinpad/PinModifyOutput.h" #include @@ -204,12 +203,9 @@ return; } - const auto cardConnection = mCardConnections.take(slotHandle); - qCInfo(ifd) << "Card successfully disconnected" << slotHandle; - const auto& response = QSharedPointer::create(slotHandle); - mDispatcher->send(response); - - Q_EMIT fireCardDisconnected(cardConnection); + qCDebug(ifd) << "Update retry counter before disconnect card for" << slotHandle; + const auto& cardConnection = mCardConnections.value(slotHandle); + cardConnection->callUpdateRetryCounterCommand(this, &ServerMessageHandlerImpl::onUpdateRetryCounterDone, slotHandle); } @@ -268,21 +264,31 @@ void ServerMessageHandlerImpl::sendEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPaceChannelOutput& pChannelOutput) { - if (pChannelOutput.getPaceReturnCode() == CardReturnCode::UNKNOWN) + ECardApiResult::Minor minor = ECardApiResult::Minor::null; + switch (pChannelOutput.getPaceReturnCode()) { - const auto& response = QSharedPointer::create(pSlotHandle, pChannelOutput, ECardApiResult::Minor::AL_Unknown_Error); - mDispatcher->send(response); - return; - } - if (pChannelOutput.getPaceReturnCode() == CardReturnCode::CARD_NOT_FOUND) - { - const auto& response = QSharedPointer::create(pSlotHandle, pChannelOutput, ECardApiResult::Minor::IFDL_Terminal_NoCard); - mDispatcher->send(response); - return; + case CardReturnCode::INPUT_TIME_OUT: + minor = ECardApiResult::Minor::IFDL_Timeout_Error; + break; + + case CardReturnCode::CANCELLATION_BY_USER: + minor = ECardApiResult::Minor::IFDL_CancellationByUser; + break; + + case CardReturnCode::CARD_NOT_FOUND: + minor = ECardApiResult::Minor::IFDL_Terminal_NoCard; + break; + + case CardReturnCode::UNKNOWN: + minor = ECardApiResult::Minor::AL_Unknown_Error; + break; + + default: + break; } - const auto& response = QSharedPointer::create(pSlotHandle, pChannelOutput); + const auto& response = QSharedPointer::create(pSlotHandle, pChannelOutput, minor); mDispatcher->send(response); } @@ -350,9 +356,6 @@ void ServerMessageHandlerImpl::sendModifyPinResponse(const QString& pSlotHandle, const ResponseApdu& pResponseApdu) { - PinModifyOutput pinModifyOutput(pResponseApdu); - const QByteArray& ccid = pinModifyOutput.toCcid(); - ECardApiResult::Minor minor = ECardApiResult::Minor::null; switch (pResponseApdu.getStatusCode()) { @@ -381,7 +384,7 @@ : ECardApiResult::Minor::AL_Unknown_Error; } - const auto& response = QSharedPointer::create(pSlotHandle, ccid, minor); + const auto& response = QSharedPointer::create(pSlotHandle, pResponseApdu, minor); mDispatcher->send(response); } @@ -457,6 +460,10 @@ void ServerMessageHandlerImpl::onClosed() { + for (const auto& cardConnection : std::as_const(mCardConnections)) + { + cardConnection->callUpdateRetryCounterCommand(this, &ServerMessageHandlerImpl::onUpdateRetryCounterDone); + } mCardConnections.clear(); Q_EMIT fireClosed(); @@ -552,4 +559,23 @@ } +void ServerMessageHandlerImpl::onUpdateRetryCounterDone(QSharedPointer pCommand) +{ + auto updateRetryCounterCommand = pCommand.staticCast(); + const QString& slotHandle = updateRetryCounterCommand->getSlotHandle(); + qCInfo(ifd) << "Update retry counter for" << slotHandle << "finished with" << pCommand->getReturnCode(); + if (slotHandle.isNull()) + { + return; + } + + const auto cardConnection = mCardConnections.take(slotHandle); + qCInfo(ifd) << "Card successfully disconnected" << slotHandle; + const auto& response = QSharedPointer::create(slotHandle); + mDispatcher->send(response); + + Q_EMIT fireCardDisconnected(cardConnection); +} + + } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ifd/base/ServerMessageHandlerImpl.h ausweisapp2-2.4.0/src/ifd/base/ServerMessageHandlerImpl.h --- ausweisapp2-2.3.1/src/ifd/base/ServerMessageHandlerImpl.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/ServerMessageHandlerImpl.h 2025-10-30 10:10:48.000000000 +0000 @@ -52,6 +52,7 @@ void onMessage(IfdMessageType pMessageType, const QJsonObject& pJsonObject); void onReaderChanged(const ReaderInfo& pInfo); void onReaderRemoved(const ReaderInfo& pInfo); + void onUpdateRetryCounterDone(QSharedPointer pCommand); public: explicit ServerMessageHandlerImpl(const QSharedPointer& pDataChannel, diff -Nru ausweisapp2-2.3.1/src/ifd/base/TlsServer.cpp ausweisapp2-2.4.0/src/ifd/base/TlsServer.cpp --- ausweisapp2-2.3.1/src/ifd/base/TlsServer.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/TlsServer.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -27,6 +27,7 @@ if (mSocket) { mSocket->deleteLater(); + mSocket.clear(); } } @@ -78,7 +79,8 @@ else { qCDebug(ifd) << "Failed to set the socket descriptor"; - delete mSocket.data(); + mSocket->deleteLater(); + mSocket.clear(); } } else @@ -102,6 +104,7 @@ { qCDebug(ifd) << "Socket error:" << pSocketError << mSocket->errorString(); mSocket->deleteLater(); + mSocket.clear(); Q_EMIT fireSocketError(pSocketError); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/Discovery.cpp ausweisapp2-2.4.0/src/ifd/base/messages/Discovery.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/Discovery.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/Discovery.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,11 +6,13 @@ #include "Discovery.h" #include "Initializer.h" +#include "PortFile.h" #include "RemoteServiceSettings.h" #include #include #include +#include Q_DECLARE_LOGGING_CATEGORY(ifd) @@ -25,6 +27,7 @@ VALUE_NAME(IFD_NAME, "IFDName") VALUE_NAME(IFD_ID, "IFDID") VALUE_NAME(PORT, "port") +VALUE_NAME(ADDRESSES, "addresses") VALUE_NAME(SUPPORTED_API, "SupportedAPI") VALUE_NAME(PAIRING, "pairing") } // namespace @@ -51,6 +54,11 @@ } const auto& array = value.toArray(); + if (array.isEmpty()) + { + markIncomplete(QStringLiteral("At least one entry is required for \"%1\"").arg(SUPPORTED_API())); + return; + } for (const QJsonValueConstRef entry : array) { if (entry.isString()) @@ -89,6 +97,64 @@ } +void Discovery::parseAddresses(const QJsonObject& pMessageObject) +{ + if (!pMessageObject.contains(ADDRESSES())) + { + return; + } + + const auto& value = pMessageObject.value(ADDRESSES()); + if (!value.isArray()) + { + invalidType(ADDRESSES(), QLatin1String("array")); + return; + } + + const auto& array = value.toArray(); + if (array.isEmpty()) + { + markIncomplete(QStringLiteral("At least one entry is required for \"%1\"").arg(ADDRESSES())); + return; + } + for (const QJsonValueConstRef entry : array) + { + if (!entry.isString()) + { + invalidType(ADDRESSES(), QLatin1String("string")); + continue; + } + + QUrl url(entry.toString(), QUrl::StrictMode); + if (!url.isValid()) + { + invalidType(ADDRESSES(), QLatin1String("url")); + continue; + } + + if (url.scheme() != QStringLiteral("wss")) + { + markIncomplete(QStringLiteral("Found \"%1\" entry with wrong scheme: %2").arg(ADDRESSES(), url.toString())); + continue; + } + + if (url.host().isEmpty()) + { + markIncomplete(QStringLiteral("Found \"%1\" entry without host: %2").arg(ADDRESSES(), url.toString())); + continue; + } + + if (url.port() == -1) + { + markIncomplete(QStringLiteral("Found \"%1\" entry without port: %2").arg(ADDRESSES(), url.toString())); + continue; + } + + mAddresses << url; + } +} + + void Discovery::parsePairing(const QJsonObject& pMessageObject) { QList sorted(mSupportedApis); @@ -114,6 +180,7 @@ , mIfdName(pIfdName.toHtmlEscaped()) , mIfdId(pIfdId) , mPort(pPort) + , mAddresses() , mSupportedApis(pSupportedApis) , mPairing(pPairing) { @@ -125,6 +192,7 @@ , mIfdName() , mIfdId() , mPort() + , mAddresses() , mSupportedApis() , mPairing() { @@ -137,10 +205,17 @@ mIfdName = getStringValue(pMessageObject, IFD_NAME()).toHtmlEscaped(); parseIfdId(pMessageObject); mPort = static_cast(getIntValue(pMessageObject, PORT(), 0)); + parseAddresses(pMessageObject); parsePairing(pMessageObject); } +bool Discovery::isSupported() const +{ + return IfdVersion(IfdVersion::selectLatestSupported(mSupportedApis)).isValid(); +} + + const QString& Discovery::getIfdName() const { return mIfdName; @@ -171,12 +246,60 @@ } -bool Discovery::getPairing() const +bool Discovery::isPairing() const { return mPairing; } +void Discovery::setAddresses(const QSet& pAddresses) +{ + if (isIncomplete()) + { + return; + } + + mAddresses.clear(); + for (const auto& address : pAddresses) + { + if (address.isNull()) + { + continue; + } + + QUrl url; + url.setScheme(QStringLiteral("wss")); + url.setHost(address.toString()); + url.setPort(mPort); + mAddresses << url; + } +} + + +bool Discovery::addressesMissing() const +{ + return mAddresses.isEmpty(); +} + + +const QSet& Discovery::getAddresses() const +{ + return mAddresses; +} + + +bool Discovery::isLocalIfd() const +{ + if (mAddresses.size() != 1) + { + return false; + } + + const auto& address = mAddresses.constBegin(); + return address->port() == PortFile::cDefaultPort && QHostAddress(address->host()).isLoopback(); +} + + QByteArray Discovery::toByteArray(IfdVersion::Version pIfdVersion, const QString&) const { QJsonObject result; @@ -193,6 +316,13 @@ } result[SUPPORTED_API()] = levels; + QJsonArray addresses; + for (const auto& address : std::as_const(mAddresses)) + { + addresses << address.toString(); + } + result[ADDRESSES()] = addresses; + if (pIfdVersion >= IfdVersion::Version::v2) { result[PAIRING()] = mPairing; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/Discovery.h ausweisapp2-2.4.0/src/ifd/base/messages/Discovery.h --- ausweisapp2-2.3.1/src/ifd/base/messages/Discovery.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/Discovery.h 2025-10-30 10:10:48.000000000 +0000 @@ -8,7 +8,10 @@ #include "IfdVersion.h" #include +#include #include +#include +#include namespace governikus @@ -20,11 +23,13 @@ QString mIfdName; QByteArray mIfdId; quint16 mPort; + QSet mAddresses; QList mSupportedApis; bool mPairing; void parseSupportedApi(const QJsonObject& pMessageObject); void parseIfdId(const QJsonObject& pMessageObject); + void parseAddresses(const QJsonObject& pMessageObject); void parsePairing(const QJsonObject& pMessageObject); public: @@ -32,13 +37,19 @@ explicit Discovery(const QJsonObject& pMessageObject); ~Discovery() override = default; + [[nodiscard]] bool isSupported() const; [[nodiscard]] const QString& getIfdName() const; [[nodiscard]] const QByteArray& getIfdId() const; [[nodiscard]] quint16 getPort() const; [[nodiscard]] const QList& getSupportedApis() const; void setPairing(bool pEnabled); - [[nodiscard]] bool getPairing() const; + [[nodiscard]] bool isPairing() const; + + void setAddresses(const QSet& pAddresses); + [[nodiscard]] bool addressesMissing() const; + [[nodiscard]] const QSet& getAddresses() const; + [[nodiscard]] bool isLocalIfd() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle = QString()) const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdConnect.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdConnect.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdConnect.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdConnect.h 2025-10-30 10:10:48.000000000 +0000 @@ -17,7 +17,7 @@ bool mExclusive; public: - IfdConnect(const QString& pSlotName, bool pExclusive = true); + explicit IfdConnect(const QString& pSlotName, bool pExclusive = true); explicit IfdConnect(const QJsonObject& pMessageObject); ~IfdConnect() override = default; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdConnectResponse.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdConnectResponse.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdConnectResponse.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdConnectResponse.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,6 @@ #include "IfdConnectResponse.h" -#include #include @@ -15,41 +14,24 @@ using namespace governikus; -namespace -{ -VALUE_NAME(SLOT_HANDLE, "SlotHandle") -} // namespace - - IfdConnectResponse::IfdConnectResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor) - : IfdMessageResponse(IfdMessageType::IFDConnectResponse, pResultMinor) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDConnectResponse, pSlotHandle, pResultMinor) { } IfdConnectResponse::IfdConnectResponse(const QJsonObject& pMessageObject) - : IfdMessageResponse(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); mError = pMessageObject.value(QLatin1String("error")).toString(); ensureType(IfdMessageType::IFDConnectResponse); } -const QString& IfdConnectResponse::getSlotHandle() const -{ - return mSlotHandle; -} - - QByteArray IfdConnectResponse::toByteArray(IfdVersion::Version, const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; - return IfdMessage::toByteArray(result); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdConnectResponse.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdConnectResponse.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdConnectResponse.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdConnectResponse.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,23 +5,22 @@ #pragma once #include "IfdMessageResponse.h" +#include "IfdSlotHandle.h" namespace governikus { class IfdConnectResponse - : public IfdMessageResponse + : public IfdSlotHandle { private: - QString mSlotHandle; QString mError; public: - IfdConnectResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); + explicit IfdConnectResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); explicit IfdConnectResponse(const QJsonObject& pMessageObject); ~IfdConnectResponse() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdDestroyPaceChannel.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdDestroyPaceChannel.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdDestroyPaceChannel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdDestroyPaceChannel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,6 @@ #include "IfdDestroyPaceChannel.h" -#include #include @@ -15,40 +14,22 @@ using namespace governikus; -namespace -{ -VALUE_NAME(SLOT_HANDLE, "SlotHandle") -} // namespace - - IfdDestroyPaceChannel::IfdDestroyPaceChannel(const QString& pSlotHandle) - : IfdMessage(IfdMessageType::IFDDestroyPACEChannel) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDDestroyPACEChannel, pSlotHandle) { } IfdDestroyPaceChannel::IfdDestroyPaceChannel(const QJsonObject& pMessageObject) - : IfdMessage(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - ensureType(IfdMessageType::IFDDestroyPACEChannel); } -const QString& IfdDestroyPaceChannel::getSlotHandle() const -{ - return mSlotHandle; -} - - QByteArray IfdDestroyPaceChannel::toByteArray(IfdVersion::Version, const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; - return IfdMessage::toByteArray(result); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdDestroyPaceChannel.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdDestroyPaceChannel.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdDestroyPaceChannel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdDestroyPaceChannel.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,22 +5,19 @@ #pragma once #include "IfdMessage.h" +#include "IfdSlotHandle.h" namespace governikus { class IfdDestroyPaceChannel - : public IfdMessage + : public IfdSlotHandle { - private: - QString mSlotHandle; - public: explicit IfdDestroyPaceChannel(const QString& pSlotHandle); explicit IfdDestroyPaceChannel(const QJsonObject& pMessageObject); ~IfdDestroyPaceChannel() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdDestroyPaceChannelResponse.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdDestroyPaceChannelResponse.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdDestroyPaceChannelResponse.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdDestroyPaceChannelResponse.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,6 @@ #include "IfdDestroyPaceChannelResponse.h" -#include #include @@ -15,40 +14,22 @@ using namespace governikus; -namespace -{ -VALUE_NAME(SLOT_HANDLE, "SlotHandle") -} // namespace - - IfdDestroyPaceChannelResponse::IfdDestroyPaceChannelResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor) - : IfdMessageResponse(IfdMessageType::IFDDestroyPACEChannelResponse, pResultMinor) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDDestroyPACEChannelResponse, pSlotHandle, pResultMinor) { } IfdDestroyPaceChannelResponse::IfdDestroyPaceChannelResponse(const QJsonObject& pMessageObject) - : IfdMessageResponse(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - ensureType(IfdMessageType::IFDDestroyPACEChannelResponse); } -const QString& IfdDestroyPaceChannelResponse::getSlotHandle() const -{ - return mSlotHandle; -} - - QByteArray IfdDestroyPaceChannelResponse::toByteArray(IfdVersion::Version, const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; - return IfdMessage::toByteArray(result); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdDestroyPaceChannelResponse.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdDestroyPaceChannelResponse.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdDestroyPaceChannelResponse.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdDestroyPaceChannelResponse.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,22 +5,19 @@ #pragma once #include "IfdMessageResponse.h" +#include "IfdSlotHandle.h" namespace governikus { class IfdDestroyPaceChannelResponse - : public IfdMessageResponse + : public IfdSlotHandle { - private: - QString mSlotHandle; - public: explicit IfdDestroyPaceChannelResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); explicit IfdDestroyPaceChannelResponse(const QJsonObject& pMessageObject); ~IfdDestroyPaceChannelResponse() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdDisconnect.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdDisconnect.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdDisconnect.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdDisconnect.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,6 @@ #include "IfdDisconnect.h" -#include #include @@ -15,40 +14,22 @@ using namespace governikus; -namespace -{ -VALUE_NAME(SLOT_HANDLE, "SlotHandle") -} // namespace - - IfdDisconnect::IfdDisconnect(const QString& pSlotHandle) - : IfdMessage(IfdMessageType::IFDDisconnect) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDDisconnect, pSlotHandle) { } IfdDisconnect::IfdDisconnect(const QJsonObject& pMessageObject) - : IfdMessage(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - ensureType(IfdMessageType::IFDDisconnect); } -const QString& IfdDisconnect::getSlotHandle() const -{ - return mSlotHandle; -} - - QByteArray IfdDisconnect::toByteArray(IfdVersion::Version, const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; - return IfdMessage::toByteArray(result); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdDisconnect.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdDisconnect.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdDisconnect.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdDisconnect.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,22 +5,19 @@ #pragma once #include "IfdMessage.h" +#include "IfdSlotHandle.h" namespace governikus { class IfdDisconnect - : public IfdMessage + : public IfdSlotHandle { - private: - QString mSlotHandle; - public: explicit IfdDisconnect(const QString& pReaderName); explicit IfdDisconnect(const QJsonObject& pMessageObject); ~IfdDisconnect() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdDisconnectResponse.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdDisconnectResponse.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdDisconnectResponse.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdDisconnectResponse.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,6 @@ #include "IfdDisconnectResponse.h" -#include #include @@ -15,40 +14,22 @@ using namespace governikus; -namespace -{ -VALUE_NAME(SLOT_HANDLE, "SlotHandle") -} // namespace - - IfdDisconnectResponse::IfdDisconnectResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor) - : IfdMessageResponse(IfdMessageType::IFDDisconnectResponse, pResultMinor) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDDisconnectResponse, pSlotHandle, pResultMinor) { } IfdDisconnectResponse::IfdDisconnectResponse(const QJsonObject& pMessageObject) - : IfdMessageResponse(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - ensureType(IfdMessageType::IFDDisconnectResponse); } -const QString& IfdDisconnectResponse::getSlotHandle() const -{ - return mSlotHandle; -} - - QByteArray IfdDisconnectResponse::toByteArray(IfdVersion::Version, const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; - return IfdMessage::toByteArray(result); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdDisconnectResponse.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdDisconnectResponse.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdDisconnectResponse.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdDisconnectResponse.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,22 +5,19 @@ #pragma once #include "IfdMessageResponse.h" +#include "IfdSlotHandle.h" namespace governikus { class IfdDisconnectResponse - : public IfdMessageResponse + : public IfdSlotHandle { - private: - QString mSlotHandle; - public: - IfdDisconnectResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); + explicit IfdDisconnectResponse(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); explicit IfdDisconnectResponse(const QJsonObject& pMessageObject); ~IfdDisconnectResponse() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdError.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdError.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdError.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdError.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,6 @@ #include "IfdError.h" -#include #include @@ -15,40 +14,22 @@ using namespace governikus; -namespace -{ -VALUE_NAME(SLOT_HANDLE, "SlotHandle") -} // namespace - - IfdError::IfdError(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor) - : IfdMessageResponse(IfdMessageType::IFDError, pResultMinor) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDError, pSlotHandle, pResultMinor) { } IfdError::IfdError(const QJsonObject& pMessageObject) - : IfdMessageResponse(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - ensureType(IfdMessageType::IFDError); } -const QString& IfdError::getSlotHandle() const -{ - return mSlotHandle; -} - - QByteArray IfdError::toByteArray(IfdVersion::Version, const QString& pContextHandle) const { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; - return IfdMessage::toByteArray(result); } diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdError.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdError.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdError.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdError.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,22 +5,19 @@ #pragma once #include "IfdMessageResponse.h" +#include "IfdSlotHandle.h" namespace governikus { class IfdError - : public IfdMessageResponse + : public IfdSlotHandle { - private: - QString mSlotHandle; - public: - IfdError(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); + explicit IfdError(const QString& pSlotHandle, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); explicit IfdError(const QJsonObject& pMessageObject); ~IfdError() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdEstablishContextResponse.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdEstablishContextResponse.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdEstablishContextResponse.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdEstablishContextResponse.h 2025-10-30 10:10:48.000000000 +0000 @@ -19,7 +19,7 @@ QString mIfdName; public: - IfdEstablishContextResponse(const QString& pIfdName, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); + explicit IfdEstablishContextResponse(const QString& pIfdName, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); explicit IfdEstablishContextResponse(const QJsonObject& pMessageObject); ~IfdEstablishContextResponse() override = default; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdEstablishPaceChannel.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdEstablishPaceChannel.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdEstablishPaceChannel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdEstablishPaceChannel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -16,7 +16,6 @@ namespace { -VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(INPUT_DATA, "InputData") VALUE_NAME(EXPECTED_PIN_LENGTH, "ExpectedPINLength") } // namespace @@ -59,8 +58,7 @@ IfdEstablishPaceChannel::IfdEstablishPaceChannel(const QString& pSlotHandle, const EstablishPaceChannel& pInputData, int pExpectedPinLength) - : IfdMessage(IfdMessageType::IFDEstablishPACEChannel) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDEstablishPACEChannel, pSlotHandle) , mInputData(pInputData) , mExpectedPinLength(pExpectedPinLength) { @@ -68,13 +66,10 @@ IfdEstablishPaceChannel::IfdEstablishPaceChannel(const QJsonObject& pMessageObject) - : IfdMessage(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) , mInputData() , mExpectedPinLength(0) { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - parseInputData(pMessageObject); if (pMessageObject.contains(EXPECTED_PIN_LENGTH())) @@ -86,12 +81,6 @@ } -const QString& IfdEstablishPaceChannel::getSlotHandle() const -{ - return mSlotHandle; -} - - const EstablishPaceChannel& IfdEstablishPaceChannel::getInputData() const { return mInputData; @@ -108,7 +97,6 @@ { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; if (pIfdVersion >= IfdVersion::Version::v2) { result[INPUT_DATA()] = QString::fromLatin1(mInputData.createInputData().toHex()); diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdEstablishPaceChannel.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdEstablishPaceChannel.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdEstablishPaceChannel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdEstablishPaceChannel.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,6 +5,7 @@ #pragma once #include "IfdMessage.h" +#include "IfdSlotHandle.h" #include "pinpad/EstablishPaceChannel.h" @@ -12,10 +13,9 @@ { class IfdEstablishPaceChannel - : public IfdMessage + : public IfdSlotHandle { private: - QString mSlotHandle; EstablishPaceChannel mInputData; int mExpectedPinLength; @@ -26,7 +26,6 @@ explicit IfdEstablishPaceChannel(const QJsonObject& pMessageObject); ~IfdEstablishPaceChannel() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] const EstablishPaceChannel& getInputData() const; [[nodiscard]] int getExpectedPinLength() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdEstablishPaceChannelResponse.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdEstablishPaceChannelResponse.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdEstablishPaceChannelResponse.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdEstablishPaceChannelResponse.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -16,7 +16,6 @@ namespace { -VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(RESULT_CODE, "ResultCode") VALUE_NAME(OUTPUT_DATA, "OutputData") } // namespace @@ -60,35 +59,50 @@ IfdEstablishPaceChannelResponse::IfdEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPaceChannelOutput& pOutputData, ECardApiResult::Minor pResultMinor) - : IfdMessageResponse(IfdMessageType::IFDEstablishPACEChannelResponse, pResultMinor) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDEstablishPACEChannelResponse, pSlotHandle, pResultMinor) , mOutputData(pOutputData) { } IfdEstablishPaceChannelResponse::IfdEstablishPaceChannelResponse(const QJsonObject& pMessageObject) - : IfdMessageResponse(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) , mOutputData() { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - parseOutputData(pMessageObject); ensureType(IfdMessageType::IFDEstablishPACEChannelResponse); } -const QString& IfdEstablishPaceChannelResponse::getSlotHandle() const +const EstablishPaceChannelOutput& IfdEstablishPaceChannelResponse::getOutputData() const { - return mSlotHandle; + return mOutputData; } -const EstablishPaceChannelOutput& IfdEstablishPaceChannelResponse::getOutputData() const +CardReturnCode IfdEstablishPaceChannelResponse::getReturnCode() const { - return mOutputData; + if (!resultHasError()) + { + return CardReturnCode::OK; + } + + switch (getResultMinor()) + { + case ECardApiResult::Minor::IFDL_Timeout_Error: + return CardReturnCode::INPUT_TIME_OUT; + + case ECardApiResult::Minor::IFDL_CancellationByUser: + return CardReturnCode::CANCELLATION_BY_USER; + + case ECardApiResult::Minor::IFDL_Terminal_NoCard: + case ECardApiResult::Minor::IFDL_InvalidSlotHandle: + return CardReturnCode::CARD_NOT_FOUND; + + default: + return CardReturnCode::COMMAND_FAILED; + } } @@ -96,7 +110,6 @@ { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; if (pIfdVersion >= IfdVersion::Version::v2) { result[RESULT_CODE()] = QString::fromLatin1(mOutputData.toResultCode().toHex()); diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdEstablishPaceChannelResponse.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdEstablishPaceChannelResponse.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdEstablishPaceChannelResponse.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdEstablishPaceChannelResponse.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,27 +5,27 @@ #pragma once #include "IfdMessageResponse.h" +#include "IfdSlotHandle.h" #include "pinpad/EstablishPaceChannelOutput.h" namespace governikus { class IfdEstablishPaceChannelResponse - : public IfdMessageResponse + : public IfdSlotHandle { private: - QString mSlotHandle; EstablishPaceChannelOutput mOutputData; void parseOutputData(const QJsonObject& pMessageObject); public: - IfdEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPaceChannelOutput& pOutputData, ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); + IfdEstablishPaceChannelResponse(const QString& pSlotHandle, const EstablishPaceChannelOutput& pOutputData, ECardApiResult::Minor pResultMinor); explicit IfdEstablishPaceChannelResponse(const QJsonObject& pMessageObject); ~IfdEstablishPaceChannelResponse() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] const EstablishPaceChannelOutput& getOutputData() const; + [[nodiscard]] CardReturnCode getReturnCode() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdModifyPin.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdModifyPin.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdModifyPin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdModifyPin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,7 +4,6 @@ #include "IfdModifyPin.h" -#include #include @@ -16,26 +15,21 @@ namespace { -VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(INPUT_DATA, "InputData") } // namespace IfdModifyPin::IfdModifyPin(const QString& pSlotHandle, const QByteArray& pInputData) - : IfdMessage(IfdMessageType::IFDModifyPIN) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDModifyPIN, pSlotHandle) , mInputData(pInputData) { } IfdModifyPin::IfdModifyPin(const QJsonObject& pMessageObject) - : IfdMessage(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) , mInputData() { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - const QString& inputData = getStringValue(pMessageObject, INPUT_DATA()); mInputData = QByteArray::fromHex(inputData.toUtf8()); @@ -45,13 +39,7 @@ bool IfdModifyPin::isValid() const { - return !mSlotHandle.isEmpty() && !mInputData.isEmpty(); -} - - -const QString& IfdModifyPin::getSlotHandle() const -{ - return mSlotHandle; + return !getSlotHandle().isEmpty() && !mInputData.isEmpty(); } @@ -65,7 +53,6 @@ { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; result[INPUT_DATA()] = QString::fromLatin1(mInputData.toHex()); return IfdMessage::toByteArray(result); diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdModifyPin.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdModifyPin.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdModifyPin.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdModifyPin.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,25 +5,24 @@ #pragma once #include "IfdMessage.h" +#include "IfdSlotHandle.h" namespace governikus { class IfdModifyPin - : public IfdMessage + : public IfdSlotHandle { private: - QString mSlotHandle; QByteArray mInputData; public: - IfdModifyPin(const QString& pSlotHandle = QString(), const QByteArray& pInputData = QByteArray()); + explicit IfdModifyPin(const QString& pSlotHandle = QString(), const QByteArray& pInputData = QByteArray()); explicit IfdModifyPin(const QJsonObject& pMessageObject); ~IfdModifyPin() override = default; [[nodiscard]] bool isValid() const; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] const QByteArray& getInputData() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdModifyPinResponse.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdModifyPinResponse.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdModifyPinResponse.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdModifyPinResponse.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,7 +4,6 @@ #include "IfdModifyPinResponse.h" -#include #include @@ -16,26 +15,21 @@ namespace { -VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(OUTPUT_DATA, "OutputData") } // namespace IfdModifyPinResponse::IfdModifyPinResponse(const QString& pSlotHandle, const QByteArray& pOutputData, ECardApiResult::Minor pResultMinor) - : IfdMessageResponse(IfdMessageType::IFDModifyPINResponse, pResultMinor) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDModifyPINResponse, pSlotHandle, pResultMinor) , mOutputData(pOutputData) { } IfdModifyPinResponse::IfdModifyPinResponse(const QJsonObject& pMessageObject) - : IfdMessageResponse(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) , mOutputData() { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - const QString& inputData = getStringValue(pMessageObject, OUTPUT_DATA()); mOutputData = QByteArray::fromHex(inputData.toUtf8()); @@ -43,12 +37,6 @@ } -const QString& IfdModifyPinResponse::getSlotHandle() const -{ - return mSlotHandle; -} - - const QByteArray& IfdModifyPinResponse::getOutputData() const { return mOutputData; @@ -96,7 +84,6 @@ { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; result[OUTPUT_DATA()] = QString::fromLatin1(mOutputData.toHex()); return IfdMessage::toByteArray(result); diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdModifyPinResponse.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdModifyPinResponse.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdModifyPinResponse.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdModifyPinResponse.h 2025-10-30 10:10:48.000000000 +0000 @@ -6,15 +6,15 @@ #include "CardReturnCode.h" #include "IfdMessageResponse.h" +#include "IfdSlotHandle.h" namespace governikus { class IfdModifyPinResponse - : public IfdMessageResponse + : public IfdSlotHandle { private: - QString mSlotHandle; QByteArray mOutputData; public: @@ -22,7 +22,6 @@ explicit IfdModifyPinResponse(const QJsonObject& pMessageObject); ~IfdModifyPinResponse() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] const QByteArray& getOutputData() const; [[nodiscard]] CardReturnCode getReturnCode() const; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdSlotHandle.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdSlotHandle.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdSlotHandle.h 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdSlotHandle.h 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "ECardApiResult.h" +#include "IfdMessage.h" + +#include +#include + + +namespace governikus +{ +template class IfdSlotHandle + : public T +{ + private: + QString mSlotHandle; + static constexpr QLatin1String cSlotHandleName = QLatin1String("SlotHandle"); + + protected: + IfdSlotHandle(IfdMessageType pType, const QString& pSlotHandle) + : T(pType) + , mSlotHandle(pSlotHandle) + { + } + + + IfdSlotHandle(IfdMessageType pType, const QString& pSlotHandle, ECardApiResult::Minor pResultMinor) + : T(pType, pResultMinor) + , mSlotHandle(pSlotHandle) + { + } + + + explicit IfdSlotHandle(const QJsonObject& pMessageObject) + : T(pMessageObject) + , mSlotHandle() + { + mSlotHandle = T::getStringValue(pMessageObject, cSlotHandleName); + } + + + [[nodiscard]] QJsonObject createMessageBody(const QString& pContextHandle) const override + { + QJsonObject messageBody = T::createMessageBody(pContextHandle); + messageBody[cSlotHandleName] = mSlotHandle; + return messageBody; + } + + public: + [[nodiscard]] const QString& getSlotHandle() const + { + return mSlotHandle; + } + + +}; + +} // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdTransmit.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdTransmit.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdTransmit.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdTransmit.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,7 +6,6 @@ #include "IfdTransmit.h" #include -#include #include @@ -18,7 +17,6 @@ namespace { -VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(COMMAND_APDUS, "CommandAPDUs") VALUE_NAME(INPUT_APDU, "InputAPDU") VALUE_NAME(DISPLAY_TEXT, "DisplayText") @@ -76,8 +74,7 @@ IfdTransmit::IfdTransmit(const QString& pSlotHandle, const QByteArray& pInputApdu, const QString& pDisplayText) - : IfdMessage(IfdMessageType::IFDTransmit) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDTransmit, pSlotHandle) , mInputApdu(pInputApdu) , mDisplayText(pDisplayText) { @@ -85,13 +82,10 @@ IfdTransmit::IfdTransmit(const QJsonObject& pMessageObject) - : IfdMessage(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) , mInputApdu() , mDisplayText() { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - parseInputApdu(pMessageObject); if (pMessageObject.contains(DISPLAY_TEXT())) @@ -103,12 +97,6 @@ } -const QString& IfdTransmit::getSlotHandle() const -{ - return mSlotHandle; -} - - const QByteArray& IfdTransmit::getInputApdu() const { return mInputApdu; @@ -125,8 +113,6 @@ { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; - if (pIfdVersion >= IfdVersion::Version::v2) { result[INPUT_APDU()] = QString::fromLatin1(mInputApdu.toHex()); diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdTransmit.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdTransmit.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdTransmit.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdTransmit.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,17 +5,15 @@ #pragma once #include "IfdMessage.h" - -#include +#include "IfdSlotHandle.h" namespace governikus { class IfdTransmit - : public IfdMessage + : public IfdSlotHandle { private: - QString mSlotHandle; QByteArray mInputApdu; QString mDisplayText; @@ -26,7 +24,6 @@ explicit IfdTransmit(const QJsonObject& pMessageObject); ~IfdTransmit() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] const QByteArray& getInputApdu() const; [[nodiscard]] const QString& getDisplayText() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdTransmitResponse.cpp ausweisapp2-2.4.0/src/ifd/base/messages/IfdTransmitResponse.cpp --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdTransmitResponse.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdTransmitResponse.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,7 +6,6 @@ #include "IfdTransmitResponse.h" #include -#include #include @@ -18,7 +17,6 @@ namespace { -VALUE_NAME(SLOT_HANDLE, "SlotHandle") VALUE_NAME(RESPONSE_APDU, "ResponseAPDU") VALUE_NAME(RESPONSE_APDUS, "ResponseAPDUs") } // namespace @@ -72,32 +70,22 @@ IfdTransmitResponse::IfdTransmitResponse(const QString& pSlotHandle, const QByteArray& pResponseApdu, ECardApiResult::Minor pResultMinor) - : IfdMessageResponse(IfdMessageType::IFDTransmitResponse, pResultMinor) - , mSlotHandle(pSlotHandle) + : IfdSlotHandle(IfdMessageType::IFDTransmitResponse, pSlotHandle, pResultMinor) , mResponseApdu(pResponseApdu) { } IfdTransmitResponse::IfdTransmitResponse(const QJsonObject& pMessageObject) - : IfdMessageResponse(pMessageObject) - , mSlotHandle() + : IfdSlotHandle(pMessageObject) , mResponseApdu() { - mSlotHandle = getStringValue(pMessageObject, SLOT_HANDLE()); - parseResponseApdu(pMessageObject); ensureType(IfdMessageType::IFDTransmitResponse); } -const QString& IfdTransmitResponse::getSlotHandle() const -{ - return mSlotHandle; -} - - const QByteArray& IfdTransmitResponse::getResponseApdu() const { return mResponseApdu; @@ -108,8 +96,6 @@ { QJsonObject result = createMessageBody(pContextHandle); - result[SLOT_HANDLE()] = mSlotHandle; - if (pIfdVersion >= IfdVersion::Version::v2) { result[RESPONSE_APDU()] = QString::fromLatin1(mResponseApdu.toHex()); diff -Nru ausweisapp2-2.3.1/src/ifd/base/messages/IfdTransmitResponse.h ausweisapp2-2.4.0/src/ifd/base/messages/IfdTransmitResponse.h --- ausweisapp2-2.3.1/src/ifd/base/messages/IfdTransmitResponse.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/base/messages/IfdTransmitResponse.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,27 +5,24 @@ #pragma once #include "IfdMessageResponse.h" - -#include +#include "IfdSlotHandle.h" namespace governikus { class IfdTransmitResponse - : public IfdMessageResponse + : public IfdSlotHandle { private: - QString mSlotHandle; QByteArray mResponseApdu; void parseResponseApdu(const QJsonObject& pMessageObject); public: - IfdTransmitResponse(const QString& pSlotHandle, const QByteArray& pResponseApdu = QByteArray(), ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); + explicit IfdTransmitResponse(const QString& pSlotHandle, const QByteArray& pResponseApdu = QByteArray(), ECardApiResult::Minor pResultMinor = ECardApiResult::Minor::null); explicit IfdTransmitResponse(const QJsonObject& pMessageObject); ~IfdTransmitResponse() override = default; - [[nodiscard]] const QString& getSlotHandle() const; [[nodiscard]] const QByteArray& getResponseApdu() const; [[nodiscard]] QByteArray toByteArray(IfdVersion::Version pIfdVersion, const QString& pContextHandle) const override; }; diff -Nru ausweisapp2-2.3.1/src/ifd/local/CMakeLists.txt ausweisapp2-2.4.0/src/ifd/local/CMakeLists.txt --- ausweisapp2-2.3.1/src/ifd/local/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/local/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -6,5 +6,7 @@ ADD_PLATFORM_LIBRARY(AusweisAppIfdLocal) target_link_libraries(AusweisAppIfdLocal ${Qt}::Core ${Qt}::WebSockets AusweisAppGlobal AusweisAppIfd) -target_link_libraries(AusweisAppIfdLocal ${Qt}::CorePrivate) +if(ANDROID) + target_link_libraries(AusweisAppIfdLocal ${Qt}::CorePrivate) +endif() target_compile_definitions(AusweisAppIfdLocal PRIVATE QT_STATICPLUGIN) diff -Nru ausweisapp2-2.3.1/src/ifd/local/LocalIfdClient.cpp ausweisapp2-2.4.0/src/ifd/local/LocalIfdClient.cpp --- ausweisapp2-2.3.1/src/ifd/local/LocalIfdClient.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/local/LocalIfdClient.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -25,6 +25,7 @@ using namespace governikus; + INIT_FUNCTION([] { ReaderManager::addMainThreadInit([] { // Force construction of LocalIfdClient in the MainThread @@ -32,6 +33,7 @@ }); }) + LocalIfdClient::LocalIfdClient() : mPsk() , mDevice() @@ -39,8 +41,9 @@ , mServiceConnection() #endif { - const Discovery discovery(QStringLiteral("LocalIfdClient"), QByteArray("LocalIfdClient"), PortFile::cDefaultPort, {IfdVersion::supported()}); - mDevice.reset(new IfdListEntry(IfdDescriptor(discovery, QHostAddress::LocalHostIPv6, true))); + Discovery discovery(QStringLiteral("LocalIfdClient"), QByteArray("LocalIfdClient"), PortFile::cDefaultPort, {IfdVersion::supported()}); + discovery.setAddresses({QHostAddress::LocalHostIPv6}); + mDevice.reset(new IfdListEntry(discovery)); } diff -Nru ausweisapp2-2.3.1/src/ifd/remote/RemoteIfdClient.cpp ausweisapp2-2.4.0/src/ifd/remote/RemoteIfdClient.cpp --- ausweisapp2-2.3.1/src/ifd/remote/RemoteIfdClient.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/remote/RemoteIfdClient.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -19,6 +19,7 @@ using namespace governikus; + INIT_FUNCTION([] { ReaderManager::addMainThreadInit([] { // Force construction of RemoteIfdClient in the MainThread @@ -26,6 +27,7 @@ }); }) + RemoteIfdClient::RemoteIfdClient() : IfdClientImpl() , mDatagramHandler() @@ -45,21 +47,25 @@ obj = IfdMessage::parseByteArray(pData); } - const Discovery discovery(obj); + Discovery discovery(obj); if (discovery.isIncomplete()) { qCDebug(ifd) << "Discarding unparsable message"; return; } - const IfdDescriptor remoteDeviceDescriptor(discovery, pAddress); - if (remoteDeviceDescriptor.isNull()) + // addresses was introduced with 2.3.2. Use source for older versions. + if (discovery.addressesMissing()) { - qCCritical(ifd) << "Dropping message from unparsable address:" << pAddress; - return; + discovery.setAddresses({pAddress}); + if (discovery.addressesMissing()) + { + qCCritical(ifd) << "Dropping message from unparsable address:" << pAddress; + return; + } } - mIfdList->update(remoteDeviceDescriptor); + mIfdList->update(discovery); } diff -Nru ausweisapp2-2.3.1/src/ifd/remote/RemoteIfdClient.h ausweisapp2-2.4.0/src/ifd/remote/RemoteIfdClient.h --- ausweisapp2-2.3.1/src/ifd/remote/RemoteIfdClient.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/remote/RemoteIfdClient.h 2025-10-30 10:10:48.000000000 +0000 @@ -8,6 +8,7 @@ #include "Env.h" #include "IfdClientImpl.h" +class MockRemoteIfdClient; class test_RemoteIfdClient; namespace governikus @@ -18,6 +19,7 @@ { Q_OBJECT friend class Env; + friend class ::MockRemoteIfdClient; friend class ::test_RemoteIfdClient; private: diff -Nru ausweisapp2-2.3.1/src/ifd/remote/RemoteIfdReaderManagerPlugin.cpp ausweisapp2-2.4.0/src/ifd/remote/RemoteIfdReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/src/ifd/remote/RemoteIfdReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/remote/RemoteIfdReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -15,6 +15,7 @@ Q_DECLARE_LOGGING_CATEGORY(card_remote) + void RemoteIfdReaderManagerPlugin::connectToPairedReaders() const { if (!mConnectToPairedReaders) @@ -39,13 +40,13 @@ const RemoteServiceSettings& remoteServiceSettings = Env::getSingleton()->getRemoteServiceSettings(); for (const QSharedPointer& remoteDevice : pRemoteDevices) { - const auto& ifdDescriptor = remoteDevice->getIfdDescriptor(); - if (!ifdDescriptor.isSupported() || ifdDescriptor.isPairingAnnounced()) + const auto& discovery = remoteDevice->getDiscovery(); + if (!discovery.isSupported() || discovery.isPairing()) { continue; } - const QByteArray ifdId = ifdDescriptor.getIfdId(); + const QByteArray ifdId = discovery.getIfdId(); // If already connected: skip. if (getDispatchers().contains(ifdId)) @@ -70,7 +71,7 @@ void RemoteIfdReaderManagerPlugin::onDeviceVanished(const QSharedPointer& pEntry) { - const auto& ifdId = pEntry->getIfdDescriptor().getIfdId(); + const auto& ifdId = pEntry->getDiscovery().getIfdId(); if (mConnectionAttempts.contains(ifdId)) { qCInfo(card_remote) << "Removing" << ifdId.toHex() << "from connection attempt list as it has vanished"; @@ -81,7 +82,7 @@ void RemoteIfdReaderManagerPlugin::onEstablishConnectionDone(const QSharedPointer& pEntry, const GlobalStatus& pStatus) { - const auto& ifdId = pEntry->getIfdDescriptor().getIfdId(); + const auto& ifdId = pEntry->getDiscovery().getIfdId(); if (mConnectionAttempts.contains(ifdId)) { qCInfo(card_remote) << "Removing" << ifdId.toHex() << "from connection attempt list as the request finished with" << pStatus; diff -Nru ausweisapp2-2.3.1/src/ifd/remote/RemoteIfdReaderManagerPlugin.h ausweisapp2-2.4.0/src/ifd/remote/RemoteIfdReaderManagerPlugin.h --- ausweisapp2-2.3.1/src/ifd/remote/RemoteIfdReaderManagerPlugin.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/remote/RemoteIfdReaderManagerPlugin.h 2025-10-30 10:10:48.000000000 +0000 @@ -42,7 +42,7 @@ ~RemoteIfdReaderManagerPlugin() override; void startScan(bool pAutoConnect) override; - void stopScan(const QString& pError = QString()) override; + void stopScan(const QString& pError) override; protected: IfdClient* getIfdClient() const override; diff -Nru ausweisapp2-2.3.1/src/ifd/remote/RemoteReaderAdvertiser.cpp ausweisapp2-2.4.0/src/ifd/remote/RemoteReaderAdvertiser.cpp --- ausweisapp2-2.3.1/src/ifd/remote/RemoteReaderAdvertiser.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/remote/RemoteReaderAdvertiser.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -41,7 +41,16 @@ void RemoteReaderAdvertiserImpl::sendDiscovery() { - mHandler->send(mDiscovery.toByteArray(IfdVersion::Version::latest)); + const auto& broadcastEntries = mHandler->getAllBroadcastEntries(); + + QSet sender; + for (const auto& broadcastEntry : broadcastEntries) + { + sender << broadcastEntry.ip(); + } + mDiscovery.setAddresses(sender); + + mHandler->send(mDiscovery.toByteArray(IfdVersion::Version::latest), broadcastEntries); } diff -Nru ausweisapp2-2.3.1/src/ifd/remote/RemoteTlsServer.cpp ausweisapp2-2.4.0/src/ifd/remote/RemoteTlsServer.cpp --- ausweisapp2-2.3.1/src/ifd/remote/RemoteTlsServer.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ifd/remote/RemoteTlsServer.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -148,8 +148,8 @@ { if (pEnable) { - std::uniform_int_distribution uni(0, 9999); - QByteArray pin = QByteArray::number(uni(Randomizer::getInstance().getGenerator())); + std::uniform_int_distribution uni(0, 9999); + QByteArray pin = QByteArray::number(uni(*Randomizer::getInstance().getGenerator())); pin.prepend(4 - pin.size(), '0'); setPsk(pin); } diff -Nru ausweisapp2-2.3.1/src/init/Bootstrap.cpp ausweisapp2-2.4.0/src/init/Bootstrap.cpp --- ausweisapp2-2.3.1/src/init/Bootstrap.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/init/Bootstrap.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -71,8 +71,8 @@ qCWarning(init) << "Linked OpenSSL Version differs:" << OpenSSL_version(OPENSSL_VERSION); } - const auto libPathes = QCoreApplication::libraryPaths(); - for (const auto& path : libPathes) + const auto libPaths = QCoreApplication::libraryPaths(); + for (const auto& path : libPaths) { qCDebug(init) << "Library path:" << path; } diff -Nru ausweisapp2-2.3.1/src/init/CMakeLists.txt ausweisapp2-2.4.0/src/init/CMakeLists.txt --- ausweisapp2-2.3.1/src/init/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/init/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,9 @@ ADD_PLATFORM_LIBRARY(AusweisAppInit) target_link_libraries(AusweisAppInit ${Qt}::Core OpenSSL::Crypto AusweisAppGlobal AusweisAppCore) -target_link_libraries(AusweisAppInit ${Qt}::CorePrivate) +if(ANDROID) + target_link_libraries(AusweisAppInit ${Qt}::CorePrivate) +endif() if(NOT INTEGRATED_SDK) target_link_libraries(AusweisAppInit ${Qt}::Gui) # QGuiApplication diff -Nru ausweisapp2-2.3.1/src/network/DatagramHandler.cpp ausweisapp2-2.4.0/src/network/DatagramHandler.cpp --- ausweisapp2-2.3.1/src/network/DatagramHandler.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/DatagramHandler.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,12 +6,15 @@ #include "Initializer.h" + using namespace governikus; + INIT_FUNCTION([] { qRegisterMetaType("QHostAddress"); }) + DatagramHandler::DatagramHandler(bool) { } diff -Nru ausweisapp2-2.3.1/src/network/DatagramHandler.h ausweisapp2-2.4.0/src/network/DatagramHandler.h --- ausweisapp2-2.3.1/src/network/DatagramHandler.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/DatagramHandler.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,9 +5,12 @@ #pragma once #include +#include + class test_DatagramHandlerImpl; + namespace governikus { @@ -20,7 +23,8 @@ explicit DatagramHandler(bool pEnableListening = true); ~DatagramHandler() override = default; [[nodiscard]] virtual bool isBound() const = 0; - virtual void send(const QByteArray& pData) = 0; + [[nodiscard]] virtual QList getAllBroadcastEntries() const = 0; + virtual void send(const QByteArray& pData, const QList& pEntries) = 0; Q_SIGNALS: void fireNewMessage(const QByteArray& pData, const QHostAddress& pAddress); diff -Nru ausweisapp2-2.3.1/src/network/DatagramHandlerImpl.cpp ausweisapp2-2.4.0/src/network/DatagramHandlerImpl.cpp --- ausweisapp2-2.3.1/src/network/DatagramHandlerImpl.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/DatagramHandlerImpl.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -9,18 +9,21 @@ #include #include #include -#include #include #include + using namespace governikus; + Q_DECLARE_LOGGING_CATEGORY(network) namespace governikus { +constexpr QLatin1StringView ipv6MulticastAddress("ff02::178"); + template<> DatagramHandler* createNewObject() { return new DatagramHandlerImpl(); @@ -40,7 +43,7 @@ : DatagramHandler(pEnableListening) , mSocket() , mMulticastLock() - , mAllAddresses() + , mAllEntries() , mFailedAddresses() , mUsedPort(pPort) , mPortFile(QStringLiteral("udp")) @@ -84,6 +87,19 @@ } else if (mSocket->bind(mUsedPort)) { +#ifndef Q_OS_MACOS + if (!mSocket->joinMulticastGroup(QHostAddress(QStringLiteral("ff02::1")))) + { + #ifdef Q_OS_WIN + qCDebug(network) << "Could not join multicast group:" << mSocket->errorString(); + #endif + } +#endif + if (!mSocket->joinMulticastGroup(QHostAddress(ipv6MulticastAddress))) + { + qCDebug(network) << "Could not join multicast group:" << mSocket->errorString(); + } + mMulticastLock.reset(new MulticastLock()); mUsedPort = mSocket->localPort(); // if user provides 0, we need to overwrite it with real value mPortFile.handlePort(mUsedPort); @@ -112,9 +128,35 @@ } -void DatagramHandlerImpl::send(const QByteArray& pData) +QList DatagramHandlerImpl::getAllBroadcastEntries() const { - sendToAllAddressEntries(pData, 0); + QList broadcastEntries; + + const auto& allInterfaces = QNetworkInterface::allInterfaces(); + for (const QNetworkInterface& interface : allInterfaces) + { + if (!isValidBroadcastInterface(interface)) + { + continue; + } + + const auto& entries = interface.addressEntries(); + for (const QNetworkAddressEntry& addressEntry : entries) + { + if (isValidAddressEntry(addressEntry)) + { + broadcastEntries << addressEntry; + } + } + } + + return broadcastEntries; +} + + +void DatagramHandlerImpl::send(const QByteArray& pData, const QList& pEntries) +{ + sendToAddressEntries(pData, pEntries, 0); } @@ -144,111 +186,44 @@ } -QList DatagramHandlerImpl::getAllBroadcastAddresses(const QNetworkInterface& pInterface) const +bool DatagramHandlerImpl::isValidAddressEntry(const QNetworkAddressEntry& pEntry) const { - QList broadcastAddresses; - - if (!isValidBroadcastInterface(pInterface)) - { - return broadcastAddresses; - } - - bool skipFurtherIPv6AddressesOnThisInterface = false; - - const auto& entries = pInterface.addressEntries(); - for (const QNetworkAddressEntry& addressEntry : entries) + const auto ipAddr = pEntry.ip(); + switch (ipAddr.protocol()) { - const auto ipAddr = addressEntry.ip(); - switch (ipAddr.protocol()) - { - case QAbstractSocket::NetworkLayerProtocol::IPv4Protocol: - { - const QHostAddress& broadcastAddr = addressEntry.broadcast(); - if (broadcastAddr.isNull() || !ipAddr.isGlobal()) - { - continue; - } + case QAbstractSocket::NetworkLayerProtocol::IPv4Protocol: + return ipAddr.isGlobal() && !pEntry.broadcast().isNull(); - broadcastAddresses += broadcastAddr; - break; - } - - case QAbstractSocket::NetworkLayerProtocol::IPv6Protocol: - { - if (skipFurtherIPv6AddressesOnThisInterface) - { - continue; - } - - const QString& scopeId = ipAddr.scopeId(); - if (scopeId.isEmpty() || !(ipAddr.isLinkLocal() || ipAddr.isUniqueLocalUnicast())) - { - continue; - } - - QHostAddress scopedMulticastAddress(QStringLiteral("ff02::1")); - scopedMulticastAddress.setScopeId(scopeId); - broadcastAddresses += scopedMulticastAddress; + case QAbstractSocket::NetworkLayerProtocol::IPv6Protocol: + return ipAddr.isGlobal(); - skipFurtherIPv6AddressesOnThisInterface = true; - break; - } + default: + qCDebug(network) << "Skipping unknown protocol type:" << ipAddr.protocol(); + return false; - default: - { - qCDebug(network) << "Skipping unknown protocol type:" << ipAddr.protocol(); - } - } } - - return broadcastAddresses; } -void DatagramHandlerImpl::sendToAllAddressEntries(const QByteArray& pData, quint16 pPort) +QHostAddress DatagramHandlerImpl::getBroadcastAddress(const QNetworkAddressEntry& pEntry) const { - const auto& allInterfaces = QNetworkInterface::allInterfaces(); - - // QNetworkInterface has no operator== so we use allAddresses() - // to check if something changed. We don't want to log - // all interfaces for any broadcast here. - const auto& addresses = QNetworkInterface::allAddresses(); - if (mAllAddresses != addresses) + const auto& ipAddr = pEntry.ip(); + switch (ipAddr.protocol()) { - mAllAddresses = addresses; - mFailedAddresses.clear(); - qCDebug(network) << "Network interfaces changed..."; - for (const auto& interface : allInterfaces) + case QAbstractSocket::NetworkLayerProtocol::IPv4Protocol: { - qCDebug(network) << interface; + return pEntry.broadcast(); } - } - QList broadcastAddresses; - for (const QNetworkInterface& interface : allInterfaces) - { - broadcastAddresses << getAllBroadcastAddresses(interface); - } - - if (broadcastAddresses.isEmpty()) - { - return; - } - - for (const QHostAddress& broadcastAddr : std::as_const(broadcastAddresses)) - { - const auto& addrString = broadcastAddr.toString(); // QTBUG-112528 - const auto alreadyFailed = mFailedAddresses.contains(addrString); - const auto sendSuccess = sendToAddress(pData, broadcastAddr, pPort, !alreadyFailed); - - if (sendSuccess && alreadyFailed) + case QAbstractSocket::NetworkLayerProtocol::IPv6Protocol: { - mFailedAddresses.removeAll(addrString); + return QHostAddress(ipv6MulticastAddress); } - else if (!sendSuccess && !alreadyFailed) + + default: { - qCDebug(network) << "Broadcasting to" << broadcastAddr << "failed"; - mFailedAddresses << addrString; + qCDebug(network) << "Skipping unknown protocol type:" << ipAddr.protocol(); + return QHostAddress(); } } } @@ -274,6 +249,57 @@ } +void DatagramHandlerImpl::sendToAddressEntries(const QByteArray& pData, const QList& pEntries, quint16 pPort) +{ + // Check if something changed because We don't want + // to log all interfaces for any broadcast here. + if (mAllEntries != pEntries) + { + mAllEntries = pEntries; + mFailedAddresses.clear(); + qCDebug(network) << "Broadcast Addresses changed..."; + for (const auto& broadcastEntry : pEntries) + { + qCDebug(network) << broadcastEntry; + } + } + + if (pEntries.isEmpty()) + { + return; + } + + QList alreadySent; + for (const auto& broadcastEntry : pEntries) + { + const auto& address = getBroadcastAddress(broadcastEntry); + if (alreadySent.contains(address)) + { + qCDebug(network) << "Skipping duplicate broadcasting to" << address; + continue; + } + + const auto alreadyFailed = mFailedAddresses.contains(address); + const auto sendSuccess = sendToAddress(pData, address, pPort, !alreadyFailed); + if (sendSuccess) + { + alreadySent << address; + if (alreadyFailed) + { + mFailedAddresses.removeAll(address); + } + continue; + } + + if (!alreadyFailed) + { + qCDebug(network) << "Broadcasting to" << address << "failed"; + mFailedAddresses << address; + } + } +} + + #if defined(Q_OS_IOS) void DatagramHandlerImpl::checkNetworkPermission() { diff -Nru ausweisapp2-2.3.1/src/network/DatagramHandlerImpl.h ausweisapp2-2.4.0/src/network/DatagramHandlerImpl.h --- ausweisapp2-2.3.1/src/network/DatagramHandlerImpl.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/DatagramHandlerImpl.h 2025-10-30 10:10:48.000000000 +0000 @@ -9,8 +9,11 @@ #include "MulticastLock.h" #include "PortFile.h" +#include +#include +#include +#include #include -#include #include @@ -27,17 +30,18 @@ private: QScopedPointer mSocket; QScopedPointer mMulticastLock; - QList mAllAddresses; - QStringList mFailedAddresses; + QList mAllEntries; + QList mFailedAddresses; quint16 mUsedPort; PortFile mPortFile; bool mEnableListening; void resetSocket(); [[nodiscard]] bool isValidBroadcastInterface(const QNetworkInterface& pInterface) const; - [[nodiscard]] QList getAllBroadcastAddresses(const QNetworkInterface& pInterface) const; + [[nodiscard]] bool isValidAddressEntry(const QNetworkAddressEntry& pEntry) const; + [[nodiscard]] QHostAddress getBroadcastAddress(const QNetworkAddressEntry& pEntry) const; [[nodiscard]] bool sendToAddress(const QByteArray& pData, const QHostAddress& pAddress, quint16 pPort = 0, bool pLogError = true); - void sendToAllAddressEntries(const QByteArray& pData, quint16 pPort); + void sendToAddressEntries(const QByteArray& pData, const QList& pEntries, quint16 pPort); #if defined(Q_OS_IOS) @@ -45,11 +49,12 @@ #endif public: - DatagramHandlerImpl(bool pEnableListening = true, quint16 pPort = HttpServer::cPort); + explicit DatagramHandlerImpl(bool pEnableListening = true, quint16 pPort = HttpServer::cPort); ~DatagramHandlerImpl() override; [[nodiscard]] bool isBound() const override; - void send(const QByteArray& pData) override; + [[nodiscard]] QList getAllBroadcastEntries() const override; + void send(const QByteArray& pData, const QList& pEntries) override; private Q_SLOTS: void onReadyRead(); diff -Nru ausweisapp2-2.3.1/src/network/HttpRequest.cpp ausweisapp2-2.4.0/src/network/HttpRequest.cpp --- ausweisapp2-2.3.1/src/network/HttpRequest.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/HttpRequest.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -133,6 +133,12 @@ } +bool HttpRequest::send(http_status pStatus) +{ + return send(HttpResponse(pStatus)); +} + + bool HttpRequest::send(const HttpResponse& pResponse) { return send(pResponse.getMessage()); diff -Nru ausweisapp2-2.3.1/src/network/HttpRequest.h ausweisapp2-2.4.0/src/network/HttpRequest.h --- ausweisapp2-2.3.1/src/network/HttpRequest.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/HttpRequest.h 2025-10-30 10:10:48.000000000 +0000 @@ -60,7 +60,7 @@ void insertHeader(); public: - HttpRequest(QTcpSocket* pSocket, QObject* pParent = nullptr); + explicit HttpRequest(QTcpSocket* pSocket, QObject* pParent = nullptr); ~HttpRequest() override; [[nodiscard]] bool isConnected() const; @@ -76,6 +76,7 @@ [[nodiscard]] quint16 getLocalPort() const; void triggerSocketBuffer(); + bool send(http_status pStatus); bool send(const HttpResponse& pResponse); bool send(const QByteArray& pResponse); diff -Nru ausweisapp2-2.3.1/src/network/HttpResponse.h ausweisapp2-2.4.0/src/network/HttpResponse.h --- ausweisapp2-2.3.1/src/network/HttpResponse.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/HttpResponse.h 2025-10-30 10:10:48.000000000 +0000 @@ -21,7 +21,7 @@ [[nodiscard]] QByteArray getStatusMessage() const; public: - HttpResponse(http_status pStatus = HTTP_STATUS_INTERNAL_SERVER_ERROR, + explicit HttpResponse(http_status pStatus = HTTP_STATUS_INTERNAL_SERVER_ERROR, const QByteArray& pBody = QByteArray(), const QByteArray& pContentType = QByteArray()); diff -Nru ausweisapp2-2.3.1/src/network/NetworkManager.cpp ausweisapp2-2.4.0/src/network/NetworkManager.cpp --- ausweisapp2-2.3.1/src/network/NetworkManager.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/NetworkManager.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -154,58 +154,70 @@ }); connect(response.data(), &QNetworkReply::sslErrors, this, [response, this](const QList& pErrors){ - QList ignoredErrors; - - for (const auto& error : pErrors) - { - if (error.error() == QSslError::OcspNoResponseFound && mUpdaterSessions.contains(response->sslConfiguration().sessionTicket())) - { - ignoredErrors << error; // QTBUG-99241 - continue; - } - - qCCritical(network) << "Fatal SSL error:" << error; - if (!error.certificate().isNull()) - { - qCCritical(network) << error.certificate(); - } - } - - if (ignoredErrors.isEmpty()) - { - response->abort(); - } - else - { - response->ignoreSslErrors(ignoredErrors); - } + onSslErrors(response, pErrors); }); connect(response.data(), &QNetworkReply::encrypted, this, [response, this]{ - const auto& cfg = response->sslConfiguration(); - TlsChecker::logSslConfig(cfg, spawnMessageLogger(network)); - - const auto& trustedCertificates = Env::getSingleton()->getUpdateCertificates(); - const auto& cert = cfg.peerCertificate(); - if (cert.isNull() || !trustedCertificates.contains(cert)) - { - const QString& textForLog = response->request().url().fileName(); - qCCritical(network).nospace() << "Untrusted certificate found [" << textForLog << "]: " << cert; - response->abort(); - return; - } - - const auto& ticket = cfg.sessionTicket(); - if (!ticket.isEmpty()) - { - mUpdaterSessions << ticket; - } + onEncryptedResponse(response); }); return response; } +void NetworkManager::onSslErrors(QSharedPointer response, const QList& pErrors) const +{ + QList ignoredErrors; + + for (const auto& error : pErrors) + { + if (error.error() == QSslError::OcspNoResponseFound && mUpdaterSessions.contains(response->sslConfiguration().sessionTicket())) + { + ignoredErrors << error; // QTBUG-99241 + continue; + } + + qCCritical(network) << "Fatal SSL error:" << error; + if (!error.certificate().isNull()) + { + qCCritical(network) << error.certificate(); + } + } + + if (ignoredErrors.isEmpty()) + { + response->abort(); + } + else + { + response->ignoreSslErrors(ignoredErrors); + } +} + + +void NetworkManager::onEncryptedResponse(QSharedPointer response) +{ + const auto& cfg = response->sslConfiguration(); + TlsChecker::logSslConfig(cfg, spawnMessageLogger(network)); + + const auto& trustedCertificates = Env::getSingleton()->getUpdateCertificates(); + const auto& cert = cfg.peerCertificate(); + if (cert.isNull() || !trustedCertificates.contains(cert)) + { + const QString& textForLog = response->request().url().fileName(); + qCCritical(network).nospace() << "Untrusted certificate found [" << textForLog << "]: " << cert; + response->abort(); + return; + } + + const auto& ticket = cfg.sessionTicket(); + if (!ticket.isEmpty()) + { + mUpdaterSessions << ticket; + } +} + + QString NetworkManager::getUserAgentServerHeader() { // According to TR-03124-1, chapter 2.2.2.1, the Server-header shall have the following form: @@ -452,7 +464,7 @@ } - QList queryProxy(const QNetworkProxyQuery& pInputQuery = QNetworkProxyQuery()) override + QList queryProxy(const QNetworkProxyQuery& pInputQuery) override { qCDebug(network) << pInputQuery; qCDebug(network) << "Found proxies" << mProxies; @@ -467,7 +479,7 @@ : public QNetworkProxyFactory { public: - QList queryProxy(const QNetworkProxyQuery& pInputQuery = QNetworkProxyQuery()) override + QList queryProxy(const QNetworkProxyQuery& pInputQuery) override { qCDebug(network) << pInputQuery; QList proxies = systemProxyForQuery(pInputQuery); @@ -482,7 +494,7 @@ : public QNetworkProxyFactory { public: - QList queryProxy(const QNetworkProxyQuery& pInputQuery = QNetworkProxyQuery()) override + QList queryProxy(const QNetworkProxyQuery& pInputQuery) override { qCDebug(network) << pInputQuery; const auto& settings = Env::getSingleton()->getGeneralSettings(); diff -Nru ausweisapp2-2.3.1/src/network/NetworkManager.h ausweisapp2-2.4.0/src/network/NetworkManager.h --- ausweisapp2-2.3.1/src/network/NetworkManager.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/NetworkManager.h 2025-10-30 10:10:48.000000000 +0000 @@ -42,6 +42,8 @@ const std::function(QNetworkRequest&)>& pInvoke); [[nodiscard]] QSharedPointer processUpdaterRequest(QNetworkRequest& pRequest, const std::function(QNetworkRequest&)>& pInvoke); + void onSslErrors(QSharedPointer response, const QList& pErrors) const; + void onEncryptedResponse(QSharedPointer response); public Q_SLOTS: void onShutdown(); diff -Nru ausweisapp2-2.3.1/src/network/PortFile.cpp ausweisapp2-2.4.0/src/network/PortFile.cpp --- ausweisapp2-2.3.1/src/network/PortFile.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/PortFile.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -9,8 +9,6 @@ using namespace governikus; -const quint16 PortFile::cDefaultPort = 24727; - QString PortFile::getPortFilename(const QString& pUsage, qint64 pPid, const QString& pApp) { const QLatin1Char sep('.'); diff -Nru ausweisapp2-2.3.1/src/network/PortFile.h ausweisapp2-2.4.0/src/network/PortFile.h --- ausweisapp2-2.3.1/src/network/PortFile.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/PortFile.h 2025-10-30 10:10:48.000000000 +0000 @@ -19,14 +19,14 @@ QFile mPortFile; public: - static const quint16 cDefaultPort; + static constexpr quint16 cDefaultPort = 24727; [[nodiscard]] static QFileInfoList getAllPortFiles(); [[nodiscard]] static QString getPortFilename(const QString& pUsage = QString(), qint64 pPid = QCoreApplication::applicationPid(), const QString& pApp = QCoreApplication::applicationName()); - PortFile(const QString& pUsage = QString(), quint16 pDefaultPort = cDefaultPort); + explicit PortFile(const QString& pUsage = QString(), quint16 pDefaultPort = cDefaultPort); ~PortFile(); void handlePort(quint16 pCurrentPort); diff -Nru ausweisapp2-2.3.1/src/network/TlsChecker.cpp ausweisapp2-2.4.0/src/network/TlsChecker.cpp --- ausweisapp2-2.3.1/src/network/TlsChecker.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/TlsChecker.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -48,11 +48,7 @@ auto keyLength = pCertificate.publicKey().length(); auto keyAlgorithm = pCertificate.publicKey().algorithm(); -#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) - qDebug() << "Check certificate key of type" << TlsChecker::toString(keyAlgorithm) << "and key size" << keyLength; -#else qDebug() << "Check certificate key of type" << keyAlgorithm << "and key size" << keyLength; -#endif return isValidKeyLength(keyLength, keyAlgorithm, pFuncMinKeySize(keyAlgorithm)); } @@ -67,11 +63,7 @@ qWarning() << "Qt failed to determine algorithm"; } -#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) - qDebug() << "Check ephemeral key of type" << TlsChecker::toString(keyAlgorithm) << "and key size" << keyLength; -#else qDebug() << "Check ephemeral key of type" << keyAlgorithm << "and key size" << keyLength; -#endif return isValidKeyLength(keyLength, keyAlgorithm, pFuncMinKeySize(keyAlgorithm)); } @@ -79,11 +71,7 @@ FailureCode::FailureInfoMap TlsChecker::getEphemeralKeyInfoMap(const QSslKey& pEphemeralServerKey) { FailureCode::FailureInfoMap infoMap; -#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) - infoMap.insert(FailureCode::Info::Ephemeral_Server_Key_Algorithm, TlsChecker::toString(pEphemeralServerKey.algorithm())); -#else infoMap.insert(FailureCode::Info::Ephemeral_Server_Key_Algorithm, getEnumName(pEphemeralServerKey.algorithm())); -#endif infoMap.insert(FailureCode::Info::Ephemeral_Server_Key_Length, QString::number(pEphemeralServerKey.length())); return infoMap; } @@ -110,11 +98,7 @@ bool sufficient = pKeyLength >= pMinKeySize; if (!sufficient) { -#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) - auto keySizeError = QStringLiteral("%1 key with insufficient key size found %2").arg(toString(pKeyAlgorithm)).arg(pKeyLength); -#else auto keySizeError = QStringLiteral("%1 key with insufficient key size found %2").arg(getEnumName(pKeyAlgorithm)).arg(pKeyLength); -#endif if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) { qCWarning(developermode).noquote() << keySizeError; @@ -129,95 +113,9 @@ } -#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) -QString TlsChecker::toString(QSsl::SslProtocol pProtocol) -{ - switch (pProtocol) - { - case QSsl::SslProtocol::AnyProtocol: - return QStringLiteral("AnyProtocol"); - - case QSsl::SslProtocol::SecureProtocols: - return QStringLiteral("SecureProtocols"); - - QT_WARNING_PUSH - QT_WARNING_DISABLE_DEPRECATED - - case QSsl::SslProtocol::TlsV1_0: - return QStringLiteral("TlsV1_0"); - - case QSsl::SslProtocol::TlsV1_0OrLater: - return QStringLiteral("TlsV1_0OrLater"); - - case QSsl::SslProtocol::TlsV1_1: - return QStringLiteral("TlsV1_1"); - - case QSsl::SslProtocol::TlsV1_1OrLater: - return QStringLiteral("TlsV1_1OrLater"); - - case QSsl::SslProtocol::DtlsV1_0: - return QStringLiteral("DtlsV1_0"); - - case QSsl::SslProtocol::DtlsV1_0OrLater: - return QStringLiteral("DtlsV1_0OrLater"); - - case QSsl::SslProtocol::DtlsV1_2: - return QStringLiteral("DtlsV1_2"); - - case QSsl::SslProtocol::DtlsV1_2OrLater: - return QStringLiteral("DtlsV1_2OrLater"); - - QT_WARNING_POP - - case QSsl::SslProtocol::TlsV1_2: - return QStringLiteral("TlsV1_2"); - - case QSsl::SslProtocol::TlsV1_2OrLater: - return QStringLiteral("TlsV1_2OrLater"); - - case QSsl::SslProtocol::TlsV1_3: - return QStringLiteral("TlsV1_3"); - - case QSsl::SslProtocol::TlsV1_3OrLater: - return QStringLiteral("TlsV1_3OrLater"); - - case QSsl::SslProtocol::UnknownProtocol: - return QStringLiteral("UnknownProtocol"); - } - - Q_UNREACHABLE(); -} - - -QString TlsChecker::toString(QSsl::KeyAlgorithm pKeyAlgorithm) -{ - switch (pKeyAlgorithm) - { - case QSsl::KeyAlgorithm::Opaque: - return QStringLiteral("Opaque"); - - case QSsl::KeyAlgorithm::Dsa: - return QStringLiteral("Dsa"); - - case QSsl::KeyAlgorithm::Rsa: - return QStringLiteral("Rsa"); - - case QSsl::KeyAlgorithm::Ec: - return QStringLiteral("Ec"); - - case QSsl::KeyAlgorithm::Dh: - return QStringLiteral("Dh"); - } - - return QStringLiteral("Unknown (%1)").arg(pKeyAlgorithm); -} - - -#endif - QStringList TlsChecker::getFatalErrors(const QList& pErrors) { - static const QSet fatalErrors( + static const QSet fatalErrors( { QSslError::CertificateSignatureFailed, QSslError::CertificateNotYetValid, QSslError::CertificateExpired, QSslError::InvalidNotBeforeField, QSslError::InvalidNotAfterField, QSslError::CertificateRevoked, QSslError::InvalidCaCertificate, @@ -292,11 +190,7 @@ void TlsChecker::logSslConfig(const QSslConfiguration& pCfg, const MessageLogger& pLogger) { pLogger.info() << "Used session cipher" << pCfg.sessionCipher(); -#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) - pLogger.info() << "Used session protocol:" << toString(pCfg.sessionProtocol()); -#else pLogger.info() << "Used session protocol:" << pCfg.sessionProtocol(); -#endif { auto stream = pLogger.info(); diff -Nru ausweisapp2-2.3.1/src/network/TlsChecker.h ausweisapp2-2.4.0/src/network/TlsChecker.h --- ausweisapp2-2.3.1/src/network/TlsChecker.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/TlsChecker.h 2025-10-30 10:10:48.000000000 +0000 @@ -31,11 +31,6 @@ public: static void logSslConfig(const QSslConfiguration& pCfg, const MessageLogger& pLogger); -#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) - [[nodiscard]] static QString toString(QSsl::SslProtocol pProtocol); - [[nodiscard]] static QString toString(QSsl::KeyAlgorithm pKeyAlgorithm); -#endif - [[nodiscard]] static QStringList getFatalErrors(const QList& pErrors); [[nodiscard]] static bool containsFatalError(const QSharedPointer& pReply, const QList& pErrors); [[nodiscard]] static QString sslErrorsToString(const QList& pErrors); diff -Nru ausweisapp2-2.3.1/src/network/UrlUtil.cpp ausweisapp2-2.4.0/src/network/UrlUtil.cpp --- ausweisapp2-2.3.1/src/network/UrlUtil.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/UrlUtil.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -19,6 +19,19 @@ using namespace governikus; +QUrl UrlUtil::resolveRedirect(const QSharedPointer& pReply) +{ + const QUrl& redirectUrl = pReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + if (redirectUrl.isEmpty() || !redirectUrl.isValid() || !redirectUrl.isRelative()) + { + return redirectUrl; + } + + const auto& requestUrl = pReply->request().url(); + return requestUrl.resolved(redirectUrl); +} + + QUrl UrlUtil::getUrlOrigin(const QUrl& pUrl) { // get default port for scheme @@ -72,7 +85,7 @@ } -QPair UrlUtil::getRequest(const QUrlQuery& pUrl) +std::pair UrlUtil::getRequest(const QUrlQuery& pUrl) { const auto queryItems = pUrl.queryItems(); for (const auto& [key, value] : queryItems) diff -Nru ausweisapp2-2.3.1/src/network/UrlUtil.h ausweisapp2-2.4.0/src/network/UrlUtil.h --- ausweisapp2-2.3.1/src/network/UrlUtil.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/network/UrlUtil.h 2025-10-30 10:10:48.000000000 +0000 @@ -6,10 +6,13 @@ #include "EnumHelper.h" +#include +#include #include #include #include +#include namespace governikus { @@ -28,6 +31,8 @@ ~UrlUtil() = delete; public: + static QUrl resolveRedirect(const QSharedPointer& pReply); + /*! * Determines the URL origin, i.e. the protocol, host name and port part of the full URL. */ @@ -39,7 +44,7 @@ static bool isMatchingSameOriginPolicy(const QUrl& pUrl1, const QUrl& pUrl2); static void setHiddenSettings(const QUrlQuery& pUrl); - static QPair getRequest(const QUrlQuery& pUrl); + static std::pair getRequest(const QUrlQuery& pUrl); template static T prepareToEnum(const QString& pStr, T pDefault) diff -Nru ausweisapp2-2.3.1/src/secure_storage/SecureStorage.cpp ausweisapp2-2.4.0/src/secure_storage/SecureStorage.cpp --- ausweisapp2-2.3.1/src/secure_storage/SecureStorage.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/secure_storage/SecureStorage.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -251,14 +251,14 @@ mMinKeySizesIfd = readKeySizes(obj, CONFIGURATION_NAME_SIZES_IFD_MIN()); mCreateKeySizeIfd = obj.value(CONFIGURATION_NAME_SIZES_IFD_CREATE()).toInt(); - mSelfAuthenticationUrl = readGroup(config, CONFIGURATION_GROUP_NAME_SELF_AUTHENTICATION(), CONFIGURATION_NAME_SELF_AUTHENTICATION_URL()); - mSelfAuthenticationTestUrl = readGroup(config, CONFIGURATION_GROUP_NAME_SELF_AUTHENTICATION(), CONFIGURATION_NAME_SELF_AUTHENTICATION_TEST_URL()); + mSelfAuthenticationUrl = QUrl(readGroup(config, CONFIGURATION_GROUP_NAME_SELF_AUTHENTICATION(), CONFIGURATION_NAME_SELF_AUTHENTICATION_URL())); + mSelfAuthenticationTestUrl = QUrl(readGroup(config, CONFIGURATION_GROUP_NAME_SELF_AUTHENTICATION(), CONFIGURATION_NAME_SELF_AUTHENTICATION_TEST_URL())); - mUpdateServerBaseUrl = readGroup(config, CONFIGURATION_GROUP_NAME_UPDATE_SERVER(), CONFIGURATION_NAME_UPDATE_SERVER_BASE_URL()); - mWhitelistServerBaseUrl = readGroup(config, CONFIGURATION_GROUP_NAME_WHITELIST_SERVER(), CONFIGURATION_NAME_WHITELIST_SERVER_BASE_URL()); + mUpdateServerBaseUrl = QUrl(readGroup(config, CONFIGURATION_GROUP_NAME_UPDATE_SERVER(), CONFIGURATION_NAME_UPDATE_SERVER_BASE_URL())); + mWhitelistServerBaseUrl = QUrl(readGroup(config, CONFIGURATION_GROUP_NAME_WHITELIST_SERVER(), CONFIGURATION_NAME_WHITELIST_SERVER_BASE_URL())); - mAppcastUpdateUrl = readGroup(config, CONFIGURATION_GROUP_NAME_UPDATES(), CONFIGURATION_NAME_APPCAST_UPDATE_URL()); - mAppcastBetaUpdateUrl = readGroup(config, CONFIGURATION_GROUP_NAME_UPDATES(), CONFIGURATION_NAME_APPCAST_BETA_UPDATE_URL()); + mAppcastUpdateUrl = QUrl(readGroup(config, CONFIGURATION_GROUP_NAME_UPDATES(), CONFIGURATION_NAME_APPCAST_UPDATE_URL())); + mAppcastBetaUpdateUrl = QUrl(readGroup(config, CONFIGURATION_GROUP_NAME_UPDATES(), CONFIGURATION_NAME_APPCAST_BETA_UPDATE_URL())); mSmartPersonalizationUrl = readGroup(config, CONFIGURATION_GROUP_NAME_SMART(), CONFIGURATION_NAME_SMART_PERSONALIZATION_URL()); mSmartPersonalizationTestUrl = readGroup(config, CONFIGURATION_GROUP_NAME_SMART(), CONFIGURATION_NAME_SMART_PERSONALIZATION_TEST_URL()); diff -Nru ausweisapp2-2.3.1/src/secure_storage/TlsConfiguration.cpp ausweisapp2-2.4.0/src/secure_storage/TlsConfiguration.cpp --- ausweisapp2-2.3.1/src/secure_storage/TlsConfiguration.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/secure_storage/TlsConfiguration.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -85,10 +85,7 @@ mConfiguration.setCiphers(ciphers); mConfiguration.setEllipticCurves(ellipticCurves); mConfiguration.setBackendConfigurationOption(QByteArrayLiteral("SignatureAlgorithms"), signatureAlgorithms.join(':')); - -#if defined(GOVERNIKUS_QT) || (QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)) mConfiguration.setDiffieHellmanParameters(QSslDiffieHellmanParameters()); // use SSL_CTX_set_dh_auto -#endif } diff -Nru ausweisapp2-2.3.1/src/services/AppUpdateData.cpp ausweisapp2-2.4.0/src/services/AppUpdateData.cpp --- ausweisapp2-2.3.1/src/services/AppUpdateData.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/services/AppUpdateData.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -23,8 +23,6 @@ , mUrl() , mSize(-1) , mChecksumUrl() - , mNotesUrl() - , mNotes() , mChecksumAlgorithm(QCryptographicHash::Sha256) , mChecksum() , mChecksumValid(false) @@ -69,7 +67,6 @@ mMinOsVersion = QVersionNumber::fromString(jsonObject[QLatin1String("minimum_platform")].toString()); mVersion = jsonObject[QLatin1String("version")].toString(); mUrl = QUrl(jsonObject[QLatin1String("url")].toString()); - mNotesUrl = QUrl(jsonObject[QLatin1String("notes")].toString()); mDate = QDateTime::fromString(jsonObject[QLatin1String("date")].toString(), Qt::ISODate); mChecksumUrl = QUrl(jsonObject[QLatin1String("checksum")].toString()); mSize = std::max(-1, jsonObject[QLatin1String("size")].toInt(-1)); @@ -96,8 +93,7 @@ mUrl.isValid() && !mUrl.fileName().isEmpty() && mChecksumUrl.isValid() && - !mChecksumUrl.fileName().isEmpty() && - mNotesUrl.isValid(); + !mChecksumUrl.fileName().isEmpty(); } @@ -149,24 +145,6 @@ } -const QUrl& AppUpdateData::getNotesUrl() const -{ - return mNotesUrl; -} - - -void AppUpdateData::setNotes(const QString& pNotes) -{ - mNotes = pNotes; -} - - -const QString& AppUpdateData::getNotes() const -{ - return mNotes; -} - - void AppUpdateData::setChecksum(const QByteArray& pChecksum, QCryptographicHash::Algorithm pAlgorithm) { if (mChecksum == pChecksum && mChecksumAlgorithm == pAlgorithm) diff -Nru ausweisapp2-2.3.1/src/services/AppUpdateData.h ausweisapp2-2.4.0/src/services/AppUpdateData.h --- ausweisapp2-2.3.1/src/services/AppUpdateData.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/services/AppUpdateData.h 2025-10-30 10:10:48.000000000 +0000 @@ -25,8 +25,6 @@ QUrl mUrl; int mSize; QUrl mChecksumUrl; - QUrl mNotesUrl; - QString mNotes; QCryptographicHash::Algorithm mChecksumAlgorithm; QByteArray mChecksum; bool mChecksumValid; @@ -50,10 +48,6 @@ [[nodiscard]] const QUrl& getUrl() const; [[nodiscard]] int getSize() const; [[nodiscard]] const QUrl& getChecksumUrl() const; - [[nodiscard]] const QUrl& getNotesUrl() const; - - void setNotes(const QString& pNotes); - [[nodiscard]] const QString& getNotes() const; void setChecksum(const QByteArray& pChecksum, QCryptographicHash::Algorithm pAlgorithm); [[nodiscard]] const QByteArray& getChecksum() const; diff -Nru ausweisapp2-2.3.1/src/services/AppUpdater.cpp ausweisapp2-2.4.0/src/services/AppUpdater.cpp --- ausweisapp2-2.3.1/src/services/AppUpdater.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/services/AppUpdater.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -24,7 +24,7 @@ : mAppUpdateJsonUrl() , mAppUpdateData() , mDownloadPath(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1Char('/')) - , mDownloadInProgress(false) + , mDownloadType(DownloadType::NONE) { const auto* secureStorage = Env::getSingleton(); @@ -35,13 +35,13 @@ bool AppUpdater::checkAppUpdate() { mAppUpdateData = AppUpdateData(); - return download(mAppUpdateJsonUrl); + return download(DownloadType::UPDATEINFO); } -bool AppUpdater::download(const QUrl& pUrl) +bool AppUpdater::download(DownloadType pDownloadType) { - if (mDownloadInProgress) + if (mDownloadType != DownloadType::NONE) { return false; } @@ -52,12 +52,56 @@ connect(downloader, &Downloader::fireDownloadFailed, this, &AppUpdater::onDownloadFailed); connect(downloader, &Downloader::fireDownloadUnnecessary, this, &AppUpdater::onDownloadUnnecessary); - mDownloadInProgress = true; - downloader->download(pUrl); + const auto url = urlFromDownloadType(pDownloadType); + if (!url.isValid()) + { + clearDownloaderConnection(); + return false; + } + + setDownloadType(pDownloadType); + downloader->download(url); return true; } +void AppUpdater::setDownloadType(DownloadType pDownloadType) +{ + if (mDownloadType == pDownloadType) + { + return; + } + + mDownloadType = pDownloadType; + Q_EMIT fireDownloadTypeChanged(); +} + + +QUrl AppUpdater::urlFromDownloadType(DownloadType pDownloadType) const +{ + switch (pDownloadType) + { + case DownloadType::UPDATEINFO: + return mAppUpdateJsonUrl; + + case DownloadType::CHECKSUM: + return mAppUpdateData.getChecksumUrl(); + + case DownloadType::APPLICATION: + return mAppUpdateData.getUrl(); + + default: + return QUrl(); + } +} + + +AppUpdater::DownloadType AppUpdater::getDownloadType() const +{ + return mDownloadType; +} + + QString AppUpdater::save(const QByteArray& pData, const QString& pFilename) const { const QDir dir(mDownloadPath); @@ -90,12 +134,13 @@ bool AppUpdater::abortDownload() const { - if (mDownloadInProgress) + const auto url = urlFromDownloadType(mDownloadType); + if (!url.isValid()) { - return Env::getSingleton()->abort(mAppUpdateData.getUrl()); + return false; } - return false; + return Env::getSingleton()->abort(url); } @@ -105,7 +150,7 @@ if (mAppUpdateData.isValid()) { - return download(mAppUpdateData.getChecksumUrl()); + return download(DownloadType::CHECKSUM); } return false; @@ -136,6 +181,12 @@ } +void AppUpdater::setAppUpdateJsonUrl(const QUrl& pUrl) +{ + mAppUpdateJsonUrl = pUrl; +} + + #endif @@ -157,10 +208,6 @@ { handleVersionInfoDownloadFinished(pData); } - else if (pUpdateUrl == mAppUpdateData.getNotesUrl()) - { - handleReleaseNotesDownloadFinished(pData); - } else if (pUpdateUrl == mAppUpdateData.getChecksumUrl()) { handleChecksumDownloadFinished(pUpdateUrl, pData); @@ -187,8 +234,7 @@ if (VersionNumber(version) > VersionNumber::getApplicationVersion()) { qCInfo(appupdate) << "Found new version:" << version << ", greater than old version" << QCoreApplication::applicationVersion(); - Env::getSingleton()->download(mAppUpdateData.getNotesUrl()); - return; + Q_EMIT fireAppcastCheckFinished(true, GlobalStatus::Code::No_Error); } else { @@ -204,19 +250,11 @@ } -void AppUpdater::handleReleaseNotesDownloadFinished(const QByteArray& pData) -{ - qCDebug(appupdate) << "Release notes downloaded successfully"; - mAppUpdateData.setNotes(QString::fromUtf8(pData)); - Q_EMIT fireAppcastCheckFinished(true, GlobalStatus::Code::No_Error); - clearDownloaderConnection(); -} - - void AppUpdater::handleChecksumDownloadFinished(const QUrl& pUpdateUrl, const QByteArray& pData) { qCDebug(appupdate) << "Checksum file downloaded successfully:" << pUpdateUrl.fileName(); save(pData, pUpdateUrl.fileName()); + clearDownloaderConnection(); const auto extensionSplit = pUpdateUrl.fileName().split(QLatin1Char('.'), Qt::SkipEmptyParts); const auto extension = extensionSplit.isEmpty() ? QByteArray() : extensionSplit.last().toLatin1(); @@ -229,8 +267,7 @@ mAppUpdateData.setUpdatePackagePath(package); if (mAppUpdateData.isChecksumValid()) { - clearDownloaderConnection(); - qCDebug(appupdate) << "Re-use valid package..."; + qCDebug(appupdate) << "Reuse valid package..."; Q_EMIT fireAppDownloadFinished(GlobalStatus::Code::No_Error); return; } @@ -238,7 +275,7 @@ } qCDebug(appupdate) << "Download package..."; - Env::getSingleton()->download(mAppUpdateData.getUrl()); + download(DownloadType::APPLICATION); } @@ -274,11 +311,6 @@ qCDebug(appupdate) << "App Update JSON failed:" << GlobalStatus(pErrorCode).toErrorDescription(); Q_EMIT fireAppcastCheckFinished(false, pErrorCode); } - else if (pUpdateUrl == mAppUpdateData.getNotesUrl()) - { - qCDebug(appupdate) << "Release notes download failed:" << GlobalStatus(pErrorCode).toErrorDescription(); - Q_EMIT fireAppcastCheckFinished(true, pErrorCode); - } else if (pUpdateUrl == mAppUpdateData.getChecksumUrl() || pUpdateUrl == mAppUpdateData.getUrl()) { qCDebug(appupdate) << "Download failed:" << GlobalStatus(pErrorCode).toErrorDescription() << ',' << pUpdateUrl; @@ -296,7 +328,7 @@ void AppUpdater::onDownloadUnnecessary(const QUrl& pUpdateUrl) { - if (pUpdateUrl == mAppUpdateJsonUrl || pUpdateUrl == mAppUpdateData.getNotesUrl() || pUpdateUrl == mAppUpdateData.getUrl() || pUpdateUrl == mAppUpdateData.getChecksumUrl()) + if (pUpdateUrl == mAppUpdateJsonUrl || pUpdateUrl == mAppUpdateData.getUrl() || pUpdateUrl == mAppUpdateData.getChecksumUrl()) { Q_ASSERT(false); qCCritical(appupdate) << "Got a DownloadUnnecessary from Downloader, but App Updates always have to be fresh, this should not be happening"; @@ -308,9 +340,14 @@ void AppUpdater::onDownloadProgress(const QUrl& pUpdateUrl, qint64 pBytesReceived, qint64 pBytesTotal) { - if (pUpdateUrl == mAppUpdateData.getUrl()) + const qint64 total = pBytesTotal == -1 ? mAppUpdateData.getSize() : pBytesTotal; + if (pUpdateUrl == mAppUpdateJsonUrl) + { + Q_EMIT fireAppcastProgress(pBytesReceived, total); + + } + else if (pUpdateUrl == mAppUpdateData.getUrl()) { - const qint64 total = pBytesTotal == -1 ? mAppUpdateData.getSize() : pBytesTotal; Q_EMIT fireAppDownloadProgress(pBytesReceived, total); } } @@ -319,5 +356,5 @@ void AppUpdater::clearDownloaderConnection() { Env::getSingleton()->disconnect(this); - mDownloadInProgress = false; + setDownloadType(DownloadType::NONE); } diff -Nru ausweisapp2-2.3.1/src/services/AppUpdater.h ausweisapp2-2.4.0/src/services/AppUpdater.h --- ausweisapp2-2.3.1/src/services/AppUpdater.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/services/AppUpdater.h 2025-10-30 10:10:48.000000000 +0000 @@ -15,6 +15,8 @@ namespace governikus { + + class AppUpdater : public QObject { @@ -22,11 +24,20 @@ friend class Env; friend class ::test_AppUpdatr; + public: + enum class DownloadType + { + NONE, + UPDATEINFO, + CHECKSUM, + APPLICATION + }; + private: QUrl mAppUpdateJsonUrl; AppUpdateData mAppUpdateData; QString mDownloadPath; - bool mDownloadInProgress; + DownloadType mDownloadType; static QCryptographicHash::Algorithm getHashAlgo(const QByteArray& pAlgo); @@ -34,22 +45,25 @@ ~AppUpdater() override = default; void clearDownloaderConnection(); - bool download(const QUrl& pUrl); + bool download(DownloadType pDownloadType); QString save(const QByteArray& pData, const QString& pFilename) const; + void setDownloadType(DownloadType pDownloadType); + QUrl urlFromDownloadType(DownloadType pDownloadType) const; public: bool abortDownload() const; bool downloadUpdate(); bool checkAppUpdate(); + [[nodiscard]] DownloadType getDownloadType() const; [[nodiscard]] const AppUpdateData& getUpdateData() const; #ifndef QT_NO_DEBUG [[nodiscard]] QString getDownloadPath() const; void setDownloadPath(const QString& pPath); + void setAppUpdateJsonUrl(const QUrl& pUrl); #endif void handleVersionInfoDownloadFinished(const QByteArray& pData); - void handleReleaseNotesDownloadFinished(const QByteArray& pData); void handleChecksumDownloadFinished(const QUrl& pUpdateUrl, const QByteArray& pData); void handleAppDownloadFinished(const QByteArray& pData); @@ -61,8 +75,10 @@ Q_SIGNALS: void fireAppcastCheckFinished(bool pUpdateAvailable, const GlobalStatus& pError); + void fireAppcastProgress(qint64 pBytesReceived, qint64 pBytesTotal); void fireAppDownloadFinished(const GlobalStatus& pError); void fireAppDownloadProgress(qint64 pBytesReceived, qint64 pBytesTotal); + void fireDownloadTypeChanged(); }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/services/Service.cpp ausweisapp2-2.4.0/src/services/Service.cpp --- ausweisapp2-2.3.1/src/services/Service.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/services/Service.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -77,14 +77,14 @@ void Service::onAppcastFinished(bool pUpdateAvailable, const GlobalStatus& pError) { - Q_EMIT fireAppcastFinished(pUpdateAvailable); - if (pUpdateAvailable && !pError.isError()) { const auto& updateData = Env::getSingleton()->getUpdateData(); Env::getSingleton()->setUpdateVersion(VersionNumber(updateData.getVersion())); } + Q_EMIT fireAppcastFinished(pUpdateAvailable); + if (pError.isNoError() || pError.getStatusCode() == GlobalStatus::Code::Downloader_Missing_Platform) { doAppUpdate(UpdateType::PROVIDER); diff -Nru ausweisapp2-2.3.1/src/settings/GeneralSettings.cpp ausweisapp2-2.4.0/src/settings/GeneralSettings.cpp --- ausweisapp2-2.3.1/src/settings/GeneralSettings.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/settings/GeneralSettings.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -29,6 +29,7 @@ SETTINGS_NAME(SETTINGS_NAME_UI_STARTUP_MODULE, "uiStartupModule") SETTINGS_NAME(SETTINGS_NAME_SHOW_ONBOARDING, "showOnboarding") SETTINGS_NAME(SETTINGS_NAME_REMIND_USER_TO_CLOSE, "remindToClose") +SETTINGS_NAME(SETTINGS_NAME_REMIND_USER_OF_AUTO_REDIRECT, "remindOfAutoRedirect") SETTINGS_NAME(SETTINGS_NAME_TRANSPORT_PIN_REMINDER, "transportPinReminder") SETTINGS_NAME(SETTINGS_NAME_DEVELOPER_OPTIONS, "developerOptions") SETTINGS_NAME(SETTINGS_NAME_DEVELOPER_MODE, "developerMode") @@ -292,6 +293,23 @@ save(mStore); Q_EMIT fireSettingsChanged(); } +} + + +bool GeneralSettings::isRemindUserOfAutoRedirect() const +{ + return mStore->value(SETTINGS_NAME_REMIND_USER_OF_AUTO_REDIRECT(), true).toBool(); +} + + +void GeneralSettings::setRemindUserOfAutoRedirect(bool pRemindUser) +{ + if (pRemindUser != isRemindUserOfAutoRedirect()) + { + mStore->setValue(SETTINGS_NAME_REMIND_USER_OF_AUTO_REDIRECT(), pRemindUser); + save(mStore); + Q_EMIT fireSettingsChanged(); + } } diff -Nru ausweisapp2-2.3.1/src/settings/GeneralSettings.h ausweisapp2-2.4.0/src/settings/GeneralSettings.h --- ausweisapp2-2.3.1/src/settings/GeneralSettings.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/settings/GeneralSettings.h 2025-10-30 10:10:48.000000000 +0000 @@ -63,6 +63,9 @@ [[nodiscard]] bool isRemindUserToClose() const; void setRemindUserToClose(bool pRemindUser); + [[nodiscard]] bool isRemindUserOfAutoRedirect() const; + void setRemindUserOfAutoRedirect(bool pRemindUser); + [[nodiscard]] bool isTransportPinReminder() const; void setTransportPinReminder(bool pTransportPinReminder); diff -Nru ausweisapp2-2.3.1/src/settings/KeyPair.cpp ausweisapp2-2.4.0/src/settings/KeyPair.cpp --- ausweisapp2-2.3.1/src/settings/KeyPair.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/settings/KeyPair.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -14,7 +14,8 @@ #include #include -#include + +#include using namespace governikus; @@ -24,25 +25,25 @@ { struct OpenSslCustomDeleter { - static inline void cleanup(EVP_PKEY* pData) + void operator()(EVP_PKEY* pData) const { EVP_PKEY_free(pData); } - static inline void cleanup(EVP_PKEY_CTX* pData) + void operator()(EVP_PKEY_CTX* pData) const { EVP_PKEY_CTX_free(pData); } - static inline void cleanup(BIO* pData) + void operator()(BIO* pData) const { BIO_free(pData); } - static inline void cleanup(X509_NAME* pData) + void operator()(X509_NAME* pData) const { X509_NAME_free(pData); } @@ -69,7 +70,7 @@ } else { - OpenSslCustomDeleter::cleanup(pKey); + EVP_PKEY_free(pKey); } } @@ -114,34 +115,28 @@ EVP_PKEY* KeyPair::createKey(int pKeyCtxNid, const std::function& pFunc) { - if (!Randomizer::getInstance().isSecureRandom()) - { - qCCritical(settings) << "Cannot get enough entropy"; - return nullptr; - } - - QScopedPointer pkeyCtx(EVP_PKEY_CTX_new_id(pKeyCtxNid, nullptr)); + std::unique_ptr pkeyCtx(EVP_PKEY_CTX_new_id(pKeyCtxNid, nullptr)); - if (pkeyCtx.isNull()) + if (!pkeyCtx) { qCCritical(settings) << "Cannot create EVP_PKEY_CTX"; return nullptr; } - if (EVP_PKEY_keygen_init(pkeyCtx.data()) != 1) + if (EVP_PKEY_keygen_init(pkeyCtx.get()) != 1) { qCCritical(settings) << "Cannot init key ctx"; return nullptr; } - if (pFunc && pFunc(pkeyCtx.data()) != 1) + if (pFunc && pFunc(pkeyCtx.get()) != 1) { qCCritical(settings) << "Cannot set parameters"; return nullptr; } EVP_PKEY* pkey = nullptr; - if (EVP_PKEY_keygen(pkeyCtx.data(), &pkey) != 1) + if (EVP_PKEY_keygen(pkeyCtx.get(), &pkey) != 1) { qCCritical(settings) << "Cannot generate key"; return nullptr; @@ -158,8 +153,8 @@ return nullptr; } - QScopedPointer bio(BIO_new_mem_buf(pData.constData(), -1)); - return QSharedPointer(PEM_read_bio_PrivateKey(bio.data(), nullptr, nullptr, nullptr), &EVP_PKEY_free); + std::unique_ptr bio(BIO_new_mem_buf(pData.constData(), -1)); + return QSharedPointer(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr), &EVP_PKEY_free); } @@ -170,8 +165,8 @@ return nullptr; } - QScopedPointer bio(BIO_new_mem_buf(pData.constData(), -1)); - return QSharedPointer(PEM_read_bio_X509(bio.data(), nullptr, nullptr, nullptr), &X509_free); + std::unique_ptr bio(BIO_new_mem_buf(pData.constData(), -1)); + return QSharedPointer(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr), &X509_free); } @@ -184,23 +179,23 @@ return nullptr; } - auto& randomizer = Randomizer::getInstance().getGenerator(); + auto* randomizer = Randomizer::getInstance().getGenerator(); std::uniform_int_distribution uni_long(1); std::uniform_int_distribution uni_qulonglong(1); - ASN1_INTEGER_set(X509_get_serialNumber(x509.data()), uni_long(randomizer)); + ASN1_INTEGER_set(X509_get_serialNumber(x509.data()), uni_long(*randomizer)); // see: https://tools.ietf.org/html/rfc5280#section-4.1.2.5 ASN1_TIME_set_string(X509_getm_notBefore(x509.data()), "19700101000000Z"); ASN1_TIME_set_string(X509_getm_notAfter(x509.data()), "99991231235959Z"); X509_set_pubkey(x509.data(), pPkey); - auto randomSerial = QByteArray::number(uni_qulonglong(randomizer)); - QScopedPointer name(X509_NAME_dup(X509_get_subject_name(x509.data()))); - X509_NAME_add_entry_by_txt(name.data(), "CN", MBSTRING_ASC, + auto randomSerial = QByteArray::number(uni_qulonglong(*randomizer)); + std::unique_ptr name(X509_NAME_dup(X509_get_subject_name(x509.data()))); + X509_NAME_add_entry_by_txt(name.get(), "CN", MBSTRING_ASC, reinterpret_cast(QCoreApplication::applicationName().toLatin1().constData()), -1, -1, 0); - X509_NAME_add_entry_by_txt(name.data(), "serialNumber", MBSTRING_ASC, + X509_NAME_add_entry_by_txt(name.get(), "serialNumber", MBSTRING_ASC, reinterpret_cast(randomSerial.constData()), -1, -1, 0); - X509_set_subject_name(x509.data(), name.data()); + X509_set_subject_name(x509.data(), name.get()); EVP_PKEY* signer = pPkey; if (pSignerKey && pSignerCert) @@ -208,7 +203,7 @@ signer = pSignerKey.data(); name.reset(X509_NAME_dup(X509_get_subject_name(pSignerCert.data()))); } - X509_set_issuer_name(x509.data(), name.data()); + X509_set_issuer_name(x509.data(), name.get()); if (!X509_sign(x509.data(), signer, EVP_sha256())) { @@ -222,13 +217,13 @@ QByteArray KeyPair::rewriteCertificate(X509* pX509) { - QScopedPointer bio(BIO_new(BIO_s_mem())); - PEM_write_bio_X509(bio.data(), pX509); + std::unique_ptr bio(BIO_new(BIO_s_mem())); + PEM_write_bio_X509(bio.get(), pX509); char* data = nullptr; // See macro BIO_get_mem_data(bio, &data); if BIO_ctrl fails. // We do not use this to avoid -Wold-style-cast warning - const long len = BIO_ctrl(bio.data(), BIO_CTRL_INFO, 0, &data); + const long len = BIO_ctrl(bio.get(), BIO_CTRL_INFO, 0, &data); return QByteArray(data, static_cast(len)); } diff -Nru ausweisapp2-2.3.1/src/settings/RemoteServiceSettings.cpp ausweisapp2-2.4.0/src/settings/RemoteServiceSettings.cpp --- ausweisapp2-2.3.1/src/settings/RemoteServiceSettings.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/settings/RemoteServiceSettings.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -12,7 +12,6 @@ #include #include #include -#include using namespace governikus; @@ -361,19 +360,20 @@ } auto infos = getRemoteInfos(); - QMutableListIterator iter(infos); - while (iter.hasNext()) + const auto& iter = std::find_if(infos.begin(), infos.end(), + [&pInfo](const auto& currentInfo) { + return currentInfo.getFingerprint() == pInfo.getFingerprint(); + }); + + if (iter == infos.end()) { - iter.next(); - if (iter.value().getFingerprint() == pInfo.getFingerprint()) - { - iter.setValue(pInfo); - setRemoteInfos(infos); - return true; - } + return false; + } - return false; + *iter = pInfo; + setRemoteInfos(infos); + return true; } diff -Nru ausweisapp2-2.3.1/src/settings/VolatileSettings.h ausweisapp2-2.4.0/src/settings/VolatileSettings.h --- ausweisapp2-2.3.1/src/settings/VolatileSettings.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/settings/VolatileSettings.h 2025-10-30 10:10:48.000000000 +0000 @@ -48,7 +48,7 @@ QString mSessionInProgress; public: - Messages(const QString& pSessionStarted = QString(), const QString& pSessionFailed = QString(), + explicit Messages(const QString& pSessionStarted = QString(), const QString& pSessionFailed = QString(), const QString& mSessionSucceeded = QString(), const QString& pSessionInProgress = QString()); [[nodiscard]] QString getSessionStarted() const; diff -Nru ausweisapp2-2.3.1/src/ui/aidl/AidlBinder.java ausweisapp2-2.4.0/src/ui/aidl/AidlBinder.java --- ausweisapp2-2.3.1/src/ui/aidl/AidlBinder.java 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/aidl/AidlBinder.java 2025-10-30 10:10:48.000000000 +0000 @@ -72,19 +72,18 @@ } mCallback = pCallback; - final boolean sessionIdIsSecure = isSecureRandomPsk(); LogHandler.getLogger().info("Android service: Callback connected."); try { - mCallback.sessionIdGenerated(sessionIdIsSecure ? mCallbackSessionId : null, sessionIdIsSecure); + mCallback.sessionIdGenerated(mCallbackSessionId); } catch (Throwable t) { handleClientException(t); } - return startReaderManagerScans() && sessionIdIsSecure; + return startReaderManagerScans(); } @@ -157,7 +156,6 @@ private native String resetValidSessionID(); - private native boolean isSecureRandomPsk(); private native boolean startReaderManagerScans(); private native void aidlSend(String pMessageFromClient); } diff -Nru ausweisapp2-2.3.1/src/ui/aidl/UiPluginAidl.cpp ausweisapp2-2.4.0/src/ui/aidl/UiPluginAidl.cpp --- ausweisapp2-2.3.1/src/ui/aidl/UiPluginAidl.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/aidl/UiPluginAidl.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -78,7 +78,7 @@ } -bool UiPluginAidl::isSuccessfullInitialized() const +bool UiPluginAidl::isSuccessfullyInitialized() const { return mJson; } @@ -172,7 +172,7 @@ Q_UNUSED(pObj) UiPluginAidl* plugin = UiPluginAidl::getInstance(); - if (!plugin->isSuccessfullInitialized()) + if (!plugin->isSuccessfullyInitialized()) { qCCritical(aidl) << "Cannot call AIDL plugin"; return pEnv->NewStringUTF(""); @@ -189,22 +189,13 @@ } -JNIEXPORT jboolean JNICALL Java_com_governikus_ausweisapp2_AidlBinder_isSecureRandomPsk(JNIEnv* pEnv, jobject pObj) -{ - Q_UNUSED(pEnv) - Q_UNUSED(pObj) - - return Randomizer::getInstance().isSecureRandom(); -} - - JNIEXPORT jboolean JNICALL Java_com_governikus_ausweisapp2_AidlBinder_startReaderManagerScans(JNIEnv* pEnv, jobject pObj) { Q_UNUSED(pEnv) Q_UNUSED(pObj) UiPluginAidl* plugin = UiPluginAidl::getInstance(); - if (!plugin->isSuccessfullInitialized()) + if (!plugin->isSuccessfullyInitialized()) { qCCritical(aidl) << "Cannot call AIDL plugin"; return false; @@ -224,7 +215,7 @@ pEnv->ReleaseStringUTFChars(pJson, nativeString); UiPluginAidl* plugin = UiPluginAidl::getInstance(); - if (!plugin->isSuccessfullInitialized()) + if (!plugin->isSuccessfullyInitialized()) { qCCritical(aidl) << "Cannot call AIDL plugin"; return; diff -Nru ausweisapp2-2.3.1/src/ui/aidl/UiPluginAidl.h ausweisapp2-2.4.0/src/ui/aidl/UiPluginAidl.h --- ausweisapp2-2.3.1/src/ui/aidl/UiPluginAidl.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/aidl/UiPluginAidl.h 2025-10-30 10:10:48.000000000 +0000 @@ -33,7 +33,7 @@ static UiPluginAidl* getInstance(bool pBlock = true); [[nodiscard]] bool initialize() override; - [[nodiscard]] bool isSuccessfullInitialized() const; + [[nodiscard]] bool isSuccessfullyInitialized() const; Q_INVOKABLE void onReceived(const QByteArray& pMessage); bool waitForWorkflowToFinish(); diff -Nru ausweisapp2-2.3.1/src/ui/json/messages/MsgHandlerStatus.cpp ausweisapp2-2.4.0/src/ui/json/messages/MsgHandlerStatus.cpp --- ausweisapp2-2.3.1/src/ui/json/messages/MsgHandlerStatus.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/json/messages/MsgHandlerStatus.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -24,14 +24,14 @@ case Action::AUTH: return QLatin1String("AUTH"); - case Action::PIN: + case Action::CHANGE_PIN: return QLatin1String("CHANGE_PIN"); case Action::PERSONALIZATION: return QLatin1String("PERSONALIZATION"); // SDK do NOT support those workflows - case Action::SELF: + case Action::SELF_AUTH: case Action::REMOTE_SERVICE: return QLatin1String("UNKNOWN"); } diff -Nru ausweisapp2-2.3.1/src/ui/proxy/PortWrapper.cpp ausweisapp2-2.4.0/src/ui/proxy/PortWrapper.cpp --- ausweisapp2-2.3.1/src/ui/proxy/PortWrapper.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/proxy/PortWrapper.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2022-2025 Governikus GmbH & Co. KG, Germany + */ + +#include "PortWrapper.h" + + +using namespace governikus; + + +bool PortWrapper::isEmpty() const +{ + return mPorts.isEmpty(); +} + + +void PortWrapper::invalidate() +{ + if (!isEmpty()) + { + mPorts.removeFirst(); + } +} + + +quint16 PortWrapper::fetchPort() const +{ + if (isEmpty()) + { + return 0; + } + + return mPorts.constFirst(); +} diff -Nru ausweisapp2-2.3.1/src/ui/proxy/PortWrapper.h ausweisapp2-2.4.0/src/ui/proxy/PortWrapper.h --- ausweisapp2-2.3.1/src/ui/proxy/PortWrapper.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/proxy/PortWrapper.h 2025-10-30 10:10:48.000000000 +0000 @@ -4,45 +4,39 @@ #pragma once -#include "HttpRequest.h" +#include #ifdef Q_OS_WIN #include #include #include -#else - #include #endif + namespace governikus { class PortWrapper { private: -#ifdef Q_OS_WIN - quint16 mPort; - -#else - QFileInfoList mPortFiles; -#endif + QList mPorts; #ifdef Q_OS_WIN static QString getUserOfProcessID(DWORD pPid); static QString getExecutableOfProcessID(DWORD pPid); - static quint16 getPortOfRunningProcess(const QList& pConnections, const QString& pUser, quint16 pSelfPort, const in_addr& pRemoteAddr); - static QString getUserOfConnection(const QList& pConnections, quint16 pLocalPort, quint16 pRemotePort, const in_addr& pProxyAddr); + static quint16 getPortOfRunningProcess(const MIB_TCPROW_OWNER_PID& pConnection, const QString& pUser, quint16 pLocalPort); + static QString getUserOfConnection(const QList& pConnections, quint16 pLocalPort, quint16 pPeerPort); static QList getConnections(); - static quint16 getProcessPort(quint16 pLocalPort, quint16 pRemotePort); + static quint16 getProcessPort(quint16 pLocalPort, quint16 pPeerPort); #else static quint16 readPortFile(const QString& pFile); #endif public: - explicit PortWrapper(const QSharedPointer& pRequest); + explicit PortWrapper(quint16 pLocalPort, quint16 pPeerPort = 0); [[nodiscard]] bool isEmpty() const; - quint16 fetchPort(); + [[nodiscard]] quint16 fetchPort() const; void invalidate(); }; diff -Nru ausweisapp2-2.3.1/src/ui/proxy/PortWrapper_generic.cpp ausweisapp2-2.4.0/src/ui/proxy/PortWrapper_generic.cpp --- ausweisapp2-2.3.1/src/ui/proxy/PortWrapper_generic.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/proxy/PortWrapper_generic.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -9,27 +9,32 @@ #include #include + using namespace governikus; + Q_DECLARE_LOGGING_CATEGORY(rproxy) -PortWrapper::PortWrapper(const QSharedPointer& pRequest) - : mPortFiles(PortFile::getAllPortFiles()) +PortWrapper::PortWrapper(quint16 pLocalPort, quint16 pPeerPort) + : mPorts() { - Q_UNUSED(pRequest) -} - + Q_UNUSED(pPeerPort) -bool PortWrapper::isEmpty() const -{ - return mPortFiles.isEmpty(); -} + const auto& portFiles = PortFile::getAllPortFiles(); + for (const auto& portFile : portFiles) + { + const auto& filename = portFile.absoluteFilePath(); + const auto port = readPortFile(filename); + if (port > 0 && port != pLocalPort) + { + mPorts << port; + } + qCWarning(rproxy) << "Ignore invalid port file:" << filename; + } -void PortWrapper::invalidate() -{ - mPortFiles.removeFirst(); + qCDebug(rproxy) << "Found instances on Ports:" << mPorts; } @@ -42,23 +47,4 @@ } return 0; -} - - -quint16 PortWrapper::fetchPort() -{ - for (QMutableListIterator iter(mPortFiles); iter.hasNext();) - { - const auto& filename = iter.next().absoluteFilePath(); - const auto port = readPortFile(filename); - if (port > 0) - { - return port; - } - - qCWarning(rproxy) << "Ignore invalid port file:" << filename; - iter.remove(); - } - - return 0; } diff -Nru ausweisapp2-2.3.1/src/ui/proxy/PortWrapper_win.cpp ausweisapp2-2.4.0/src/ui/proxy/PortWrapper_win.cpp --- ausweisapp2-2.3.1/src/ui/proxy/PortWrapper_win.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/proxy/PortWrapper_win.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,35 +5,48 @@ #include "PortWrapper.h" #include -#include #include + using namespace governikus; + Q_DECLARE_LOGGING_CATEGORY(rproxy) -PortWrapper::PortWrapper(const QSharedPointer& pRequest) - : mPort(getProcessPort(pRequest->getLocalPort(), pRequest->getPeerPort())) +namespace { -} +const ULONG LOCALHOST = htonl(INADDR_LOOPBACK); -bool PortWrapper::isEmpty() const -{ - return mPort == 0; -} +} // namespace -void PortWrapper::invalidate() +PortWrapper::PortWrapper(quint16 pLocalPort, quint16 pPeerPort) + : mPorts() { - mPort = 0; -} - + if (pPeerPort == 0) + { + const auto& connections = getConnections(); + for (const auto& connection : connections) + { + const auto port = getPortOfRunningProcess(connection, QString(), pLocalPort); + if (port != 0) + { + mPorts << port; + } + } + } + else + { + const auto port = getProcessPort(pLocalPort, pPeerPort); + if (port > 0) + { + mPorts << port; + } + } -quint16 PortWrapper::fetchPort() -{ - return mPort; + qCDebug(rproxy) << "Found instances on Ports:" << mPorts; } @@ -147,51 +160,46 @@ } -quint16 PortWrapper::getPortOfRunningProcess(const QList& pConnections, const QString& pUser, quint16 pSelfPort, const in_addr& pProxyAddr) +quint16 PortWrapper::getPortOfRunningProcess(const MIB_TCPROW_OWNER_PID& pConnection, const QString& pUser, quint16 pLocalPort) { - for (const auto& connection : pConnections) + if (pConnection.dwState != MIB_TCP_STATE_LISTEN + || pConnection.dwLocalAddr != LOCALHOST) { - if (connection.dwState != MIB_TCP_STATE_LISTEN - || connection.dwLocalAddr != pProxyAddr.S_un.S_addr) - { - continue; - } - - if (getUserOfProcessID(connection.dwOwningPid) != pUser) - { - continue; - } + return 0; + } - if (ntohs(static_cast(connection.dwLocalPort)) == pSelfPort) - { - continue; - } + if (!pUser.isNull() && getUserOfProcessID(pConnection.dwOwningPid) != pUser) + { + return 0; + } - const auto& executable = getExecutableOfProcessID(connection.dwOwningPid); - if (executable.isEmpty() || !QCoreApplication::applicationFilePath().endsWith(executable)) - { - continue; - } + if (ntohs(static_cast(pConnection.dwLocalPort)) == pLocalPort) + { + return 0; + } - return ntohs(static_cast(connection.dwLocalPort)); + const auto& executable = getExecutableOfProcessID(pConnection.dwOwningPid); + if (executable.isEmpty() || !QCoreApplication::applicationFilePath().endsWith(executable)) + { + return 0; } - return 0; + return ntohs(static_cast(pConnection.dwLocalPort)); } -QString PortWrapper::getUserOfConnection(const QList& pConnections, quint16 pLocalPort, quint16 pRemotePort, const in_addr& pRemoteAddr) +QString PortWrapper::getUserOfConnection(const QList& pConnections, quint16 pLocalPort, quint16 pPeerPort) { for (const auto& connection : pConnections) { if (connection.dwState != MIB_TCP_STATE_ESTAB - || connection.dwRemoteAddr != pRemoteAddr.S_un.S_addr - || connection.dwLocalAddr != pRemoteAddr.S_un.S_addr) + || connection.dwRemoteAddr != LOCALHOST + || connection.dwLocalAddr != LOCALHOST) { continue; } - if (ntohs(static_cast(connection.dwLocalPort)) == pRemotePort + if (ntohs(static_cast(connection.dwLocalPort)) == pPeerPort && ntohs(static_cast(connection.dwRemotePort)) == pLocalPort) { return getUserOfProcessID(connection.dwOwningPid); @@ -234,20 +242,23 @@ } -quint16 PortWrapper::getProcessPort(quint16 pLocalPort, quint16 pRemotePort) +quint16 PortWrapper::getProcessPort(quint16 pLocalPort, quint16 pPeerPort) { - struct in_addr localhost = { - { - {127, 0, 0, 1} - } - }; const auto& connections = getConnections(); - const auto& user = getUserOfConnection(connections, pLocalPort, pRemotePort, localhost); + const auto& user = getUserOfConnection(connections, pLocalPort, pPeerPort); if (user.isEmpty()) { qCWarning(rproxy) << "Cannot detect user of connection"; return 0; } qCDebug(rproxy) << "Detected user of request:" << user; - return getPortOfRunningProcess(connections, user, pLocalPort, localhost); + for (const auto& connection : connections) + { + const auto port = getPortOfRunningProcess(connection, user, pLocalPort); + if (port != 0) + { + return port; + } + } + return 0; } diff -Nru ausweisapp2-2.3.1/src/ui/proxy/RedirectBroadcast.cpp ausweisapp2-2.4.0/src/ui/proxy/RedirectBroadcast.cpp --- ausweisapp2-2.3.1/src/ui/proxy/RedirectBroadcast.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/proxy/RedirectBroadcast.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#include "RedirectBroadcast.h" + +#include +#include +#include +#include + + +using namespace governikus; + + +Q_DECLARE_LOGGING_CATEGORY(rproxy) + + +RedirectBroadcast::RedirectBroadcast(const QNetworkDatagram& pDatagram, quint16 pLocalPort) + : QObject() + , mPortWrapper(pLocalPort) +{ + if (mPortWrapper.isEmpty()) + { + deleteLater(); + } + else + { + redirect(pDatagram); + } +} + + +RedirectBroadcast::~RedirectBroadcast() = default; + + +void RedirectBroadcast::redirect(const QNetworkDatagram& pDatagram) +{ + QUdpSocket socket; + socket.setProxy(QNetworkProxy::NoProxy); + + quint16 port = 0; + while ((port = mPortWrapper.fetchPort()) > 0) + { + if (socket.writeDatagram(pDatagram.data(), QHostAddress::LocalHost, port)) + { + socket.flush(); + } + else + { + qCDebug(rproxy) << "Cannot redirect to port:" << port; + } + + mPortWrapper.invalidate(); + } + + deleteLater(); +} diff -Nru ausweisapp2-2.3.1/src/ui/proxy/RedirectBroadcast.h ausweisapp2-2.4.0/src/ui/proxy/RedirectBroadcast.h --- ausweisapp2-2.3.1/src/ui/proxy/RedirectBroadcast.h 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/proxy/RedirectBroadcast.h 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "PortWrapper.h" + +#include +#include + + +namespace governikus +{ +class RedirectBroadcast + : public QObject +{ + Q_OBJECT + + private: + PortWrapper mPortWrapper; + + void redirect(const QNetworkDatagram& pDatagram); + + public: + explicit RedirectBroadcast(const QNetworkDatagram& pDatagram, quint16 pLocalPort); + ~RedirectBroadcast() override; +}; + +} // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ui/proxy/RedirectRequest.cpp ausweisapp2-2.4.0/src/ui/proxy/RedirectRequest.cpp --- ausweisapp2-2.3.1/src/ui/proxy/RedirectRequest.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/proxy/RedirectRequest.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -18,7 +18,7 @@ RedirectRequest::RedirectRequest(const QSharedPointer& pRequest, QObject* pParent) : QTcpSocket(pParent) , mRequest(pRequest) - , mPortWrapper(pRequest) + , mPortWrapper(pRequest->getLocalPort(), pRequest->getPeerPort()) , mAnswerReceived(false) { Q_ASSERT(mRequest); diff -Nru ausweisapp2-2.3.1/src/ui/proxy/UiPluginProxy.cpp ausweisapp2-2.4.0/src/ui/proxy/UiPluginProxy.cpp --- ausweisapp2-2.3.1/src/ui/proxy/UiPluginProxy.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/proxy/UiPluginProxy.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,9 +5,12 @@ #include "UiPluginProxy.h" #include "Env.h" +#include "RedirectBroadcast.h" #include "RedirectRequest.h" #include +#include + using namespace governikus; @@ -35,6 +38,17 @@ if (mServer->isListening()) { + mSocket.reset(new QUdpSocket()); + connect(mSocket.data(), &QUdpSocket::readyRead, this, &UiPluginProxy::handleBroadcast); + if (mSocket->bind(mServer->getServerPort())) + { + qCDebug(rproxy) << "UDP proxy port bound:" << mSocket->localPort(); + } + else + { + qCWarning(rproxy) << "UDP proxy port failed:" << mServer->getServerPort(); + } + Q_EMIT fireUiDominationRequest(this, tr("Reverse proxy plugin is enabled")); return true; } @@ -62,6 +76,15 @@ } +void UiPluginProxy::handleBroadcast() +{ + while (mSocket->hasPendingDatagrams()) + { + new RedirectBroadcast(mSocket->receiveDatagram(), mSocket->localPort()); + } +} + + void UiPluginProxy::handleShowUiRequest(const QString& pUiModule, const QSharedPointer& pRequest) { Q_UNUSED(pUiModule) diff -Nru ausweisapp2-2.3.1/src/ui/proxy/UiPluginProxy.h ausweisapp2-2.4.0/src/ui/proxy/UiPluginProxy.h --- ausweisapp2-2.3.1/src/ui/proxy/UiPluginProxy.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/proxy/UiPluginProxy.h 2025-10-30 10:10:48.000000000 +0000 @@ -9,8 +9,12 @@ #include "HttpServer.h" #include "UiPlugin.h" +#include + + class test_UiPluginProxy; + namespace governikus { class UiPluginProxy @@ -24,11 +28,13 @@ private: QSharedPointer mServer; + QScopedPointer mSocket; [[nodiscard]] bool listen(); void handleShowUiRequest(const QString& pUiModule, const QSharedPointer& pRequest) override; void handleWorkflowRequest(const QSharedPointer& pRequest) override; + void handleBroadcast(); private Q_SLOTS: void doShutdown() override; diff -Nru ausweisapp2-2.3.1/src/ui/qml/AppUpdateDataModel.cpp ausweisapp2-2.4.0/src/ui/qml/AppUpdateDataModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/AppUpdateDataModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/AppUpdateDataModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -20,14 +20,22 @@ AppUpdateDataModel::AppUpdateDataModel() : QObject() + , mAppcastFinished(false) , mUpdateAvailable(false) , mMissingPlatform(false) , mDownloadProgress(0) , mDownloadTotal(0) + , mAppcastProgress(0) + , mAppcastTotal(0) { connect(Env::getSingleton(), &AppUpdater::fireAppcastCheckFinished, this, &AppUpdateDataModel::onAppcastFinished); + connect(Env::getSingleton(), &AppUpdater::fireAppcastProgress, this, &AppUpdateDataModel::onAppcastProgress); connect(Env::getSingleton(), &AppUpdater::fireAppDownloadProgress, this, &AppUpdateDataModel::onAppDownloadProgress); connect(Env::getSingleton(), &AppUpdater::fireAppDownloadFinished, this, &AppUpdateDataModel::onAppDownloadFinished); + connect(Env::getSingleton(), &AppUpdater::fireDownloadTypeChanged, this, &AppUpdateDataModel::fireDownloadProgressChanged); + + connect(this, &AppUpdateDataModel::fireAppUpdateDataChanged, this, &AppUpdateDataModel::fireAppcastStatusChanged); + connect(this, &AppUpdateDataModel::fireDownloadProgressChanged, this, &AppUpdateDataModel::fireAppcastStatusChanged); } @@ -43,9 +51,12 @@ return tr("The received data is broken. Check your network connection and try to restart the update."); case GlobalStatus::Code::Update_Execution_Failed: - + { + const auto& a_start = QStringLiteral("").arg(getDownloadFolder()); + const auto& a_end = QStringLiteral(""); //: INFO DESKTOP Text of the popup that is shown when the execution of the update failed (1/2). - return tr("The update could not be started automatically after a successful download. Please try to do a manual update. You can find the downloaded file %1here%2.").arg(QStringLiteral("").arg(getDownloadFolder())).arg(QStringLiteral("")); + return tr("The update could not be started automatically after a successful download. Please try to do a manual update. You can find the downloaded file %1here%2.").arg(a_start, a_end); + } default: //: INFO DESKTOP Generic text of the popup that is shown when the app download failed. @@ -59,9 +70,11 @@ { if (pCode == GlobalStatus::Code::Update_Execution_Failed) { - const auto language = Env::getSingleton()->getLanguage(); + const auto& language = Env::getSingleton()->getLanguage(); + const auto& a_start = QStringLiteral("").arg(language); + const auto& a_end = QStringLiteral(""); //: INFO DESKTOP Text of the popup that is shown when the execution of the update failed (2/2). - return tr("If this does not help, contact our %1support%2.").arg(QStringLiteral("").arg(QStringLiteral("https://www.ausweisapp.bund.de/%1/aa2/support").arg(language))).arg(QStringLiteral("")); + return tr("If this does not help, contact our %1support%2.").arg(a_start, a_end); } return QString(); } @@ -69,23 +82,35 @@ void AppUpdateDataModel::onAppcastFinished(bool pUpdateAvailable, const GlobalStatus& pStatus) { + mAppcastProgress = pStatus.isError() ? 0 : mAppcastTotal; + Q_EMIT fireDownloadProgressChanged(); + + mAppcastFinished = pStatus.getStatusCode() != GlobalStatus::Code::Downloader_Aborted; mUpdateAvailable = pUpdateAvailable; mMissingPlatform = pStatus.getStatusCode() == GlobalStatus::Code::Downloader_Missing_Platform; Q_EMIT fireAppUpdateDataChanged(); } +void AppUpdateDataModel::onAppcastProgress(qint64 pBytesReceived, qint64 pBytesTotal) +{ + mAppcastProgress = static_cast(pBytesReceived / 1000); + mAppcastTotal = static_cast(pBytesTotal / 1000); + Q_EMIT fireDownloadProgressChanged(); +} + + void AppUpdateDataModel::onAppDownloadProgress(qint64 pBytesReceived, qint64 pBytesTotal) { - mDownloadProgress = static_cast(pBytesReceived / 1024); - mDownloadTotal = static_cast(pBytesTotal / 1024); + mDownloadProgress = static_cast(pBytesReceived / 1000); + mDownloadTotal = static_cast(pBytesTotal / 1000); Q_EMIT fireDownloadProgressChanged(); } void AppUpdateDataModel::onAppDownloadFinished(const GlobalStatus& pError) { - mDownloadTotal = getSize() / 1024; + mDownloadTotal = getSize() / 1000; if (pError.isError()) { mDownloadProgress = 0; @@ -136,12 +161,6 @@ } -bool AppUpdateDataModel::isMissingPlatform() const -{ - return mMissingPlatform; -} - - bool AppUpdateDataModel::isValid() const { return Env::getSingleton()->getUpdateData().isValid(); @@ -154,6 +173,73 @@ } +int AppUpdateDataModel::getAppcastProgress() const +{ + return mAppcastProgress; +} + + +int AppUpdateDataModel::getAppcastTotal() const +{ + return mAppcastTotal; +} + + +QString AppUpdateDataModel::getAppcastStatus() const +{ + if (isAppcastRunning()) + { + //: LABEL DESKTOP + return tr("Searching for software updates..."); + } + + if (!mAppcastFinished) + { + return QString(); + } + + if (isValid()) + { + if (mUpdateAvailable) + { + //: LABEL DESKTOP An update is available, the new version is supplied to the user. + return tr("An update is available (version %1).").arg(getVersion()); + } + + //: LABEL DESKTOP %1 is replaced with the version number of the software and %2 is replaced with the application name. + return tr("Your version %1 of %2 is up to date.").arg(QCoreApplication::applicationVersion(), QCoreApplication::applicationName()); + } + + if (mMissingPlatform) + { + //: LABEL DESKTOP + return tr("An update information for your platform is not available."); + } + + if (mUpdateAvailable) + { + //: LABEL DESKTOP The updater found an update but not all required update information are valid, this should be a very rare case. + return tr("An update is available but retrieving the information failed."); + } + + //: LABEL DESKTOP + return tr("The update information could not be retrieved. Please check your network connection."); +} + + +bool AppUpdateDataModel::isAppcastRunning() const +{ + return Env::getSingleton()->getDownloadType() == AppUpdater::DownloadType::UPDATEINFO; +} + + +bool AppUpdateDataModel::isDownloadRunning() const +{ + const auto& downloadType = Env::getSingleton()->getDownloadType(); + return downloadType == AppUpdater::DownloadType::CHECKSUM || downloadType == AppUpdater::DownloadType::APPLICATION; +} + + int AppUpdateDataModel::getDownloadProgress() const { return mDownloadProgress; @@ -204,31 +290,18 @@ } -const QUrl& AppUpdateDataModel::getNotesUrl() const -{ - return Env::getSingleton()->getUpdateData().getNotesUrl(); -} - - -const QString& AppUpdateDataModel::getNotes() const -{ - return Env::getSingleton()->getUpdateData().getNotes(); -} - - bool AppUpdateDataModel::download() { mDownloadProgress = 0; - const auto success = Env::getSingleton()->downloadUpdate(); Q_EMIT fireDownloadProgressChanged(); - return success; + return Env::getSingleton()->downloadUpdate(); } bool AppUpdateDataModel::abortDownload() { - mDownloadProgress = 0; const auto success = Env::getSingleton()->abortDownload(); + mDownloadProgress = 0; Q_EMIT fireDownloadProgressChanged(); return success; } diff -Nru ausweisapp2-2.3.1/src/ui/qml/AppUpdateDataModel.h ausweisapp2-2.4.0/src/ui/qml/AppUpdateDataModel.h --- ausweisapp2-2.3.1/src/ui/qml/AppUpdateDataModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/AppUpdateDataModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -25,53 +25,64 @@ friend class ::test_AppUpdateDataModel; Q_PROPERTY(bool updateAvailable READ isUpdateAvailable NOTIFY fireAppUpdateDataChanged) - Q_PROPERTY(bool missingPlatform READ isMissingPlatform NOTIFY fireAppUpdateDataChanged) Q_PROPERTY(bool valid READ isValid NOTIFY fireAppUpdateDataChanged) Q_PROPERTY(bool compatible READ isCompatible NOTIFY fireAppUpdateDataChanged) + Q_PROPERTY(bool appcastRunning READ isAppcastRunning NOTIFY fireDownloadProgressChanged) + Q_PROPERTY(bool downloadRunning READ isDownloadRunning NOTIFY fireDownloadProgressChanged) + Q_PROPERTY(int appcastProgress READ getAppcastProgress NOTIFY fireDownloadProgressChanged) + Q_PROPERTY(int appcastTotal READ getAppcastTotal NOTIFY fireDownloadProgressChanged) + Q_PROPERTY(QString appcastStatus READ getAppcastStatus NOTIFY fireAppcastStatusChanged) Q_PROPERTY(int downloadProgress READ getDownloadProgress NOTIFY fireDownloadProgressChanged) Q_PROPERTY(int downloadTotal READ getDownloadTotal NOTIFY fireDownloadProgressChanged) - Q_PROPERTY(QString downloadFolder READ getDownloadFolder NOTIFY fireDownloadProgressChanged) 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: + bool mAppcastFinished; bool mUpdateAvailable; bool mMissingPlatform; int mDownloadProgress; int mDownloadTotal; - - AppUpdateDataModel(); - ~AppUpdateDataModel() override = default; + int mAppcastProgress; + int mAppcastTotal; QString errorFromStatusCode(GlobalStatus::Code pCode) const; QString supportInfoFromStatusCode(GlobalStatus::Code pCode) const; + [[nodiscard]] QString getDownloadFolder() const; + +#ifndef QT_NO_DEBUG + + public: +#endif + AppUpdateDataModel(); + ~AppUpdateDataModel() override = default; private Q_SLOTS: void onAppcastFinished(bool pUpdateAvailable, const GlobalStatus& pStatus); + void onAppcastProgress(qint64 pBytesReceived, qint64 pBytesTotal); void onAppDownloadProgress(qint64 pBytesReceived, qint64 pBytesTotal); void onAppDownloadFinished(const GlobalStatus& pError); public: [[nodiscard]] bool isUpdateAvailable() const; - [[nodiscard]] bool isMissingPlatform() const; [[nodiscard]] bool isValid() const; [[nodiscard]] bool isCompatible() const; + [[nodiscard]] bool isAppcastRunning() const; + [[nodiscard]] bool isDownloadRunning() const; + [[nodiscard]] int getAppcastProgress() const; + [[nodiscard]] int getAppcastTotal() const; + [[nodiscard]] QString getAppcastStatus() const; [[nodiscard]] int getDownloadProgress() const; [[nodiscard]] int getDownloadTotal() const; - [[nodiscard]] QString getDownloadFolder() const; [[nodiscard]] const QDateTime& getDate() const; [[nodiscard]] const QString& getVersion() const; [[nodiscard]] const QUrl& getUrl() const; [[nodiscard]] int getSize() const; [[nodiscard]] const QUrl& getChecksumUrl() const; - [[nodiscard]] const QUrl& getNotesUrl() const; - [[nodiscard]] const QString& getNotes() const; [[nodiscard]] Q_INVOKABLE bool download(); [[nodiscard]] Q_INVOKABLE bool abortDownload(); @@ -79,8 +90,9 @@ void fireAppUpdateDataChanged(); void fireDownloadProgressChanged(); void fireAppUpdateAborted(); - void fireAppUpdateFailed(QString pError, QString pSupportInfo); + void fireAppUpdateFailed(const QString& pError, const QString& pSupportInfo); void fireAppDownloadFinished(); + void fireAppcastStatusChanged(); }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ui/qml/ApplicationModel.cpp ausweisapp2-2.4.0/src/ui/qml/ApplicationModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/ApplicationModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/ApplicationModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -39,6 +39,18 @@ Q_DECLARE_LOGGING_CATEGORY(feedback) +void ApplicationModel::notifyScreenReaderChangedThreadSafe() +{ + QMetaObject::invokeMethod(QCoreApplication::instance(), [] { + auto* applicationModel = Env::getSingleton(); + if (applicationModel) + { + Q_EMIT applicationModel->fireScreenReaderRunningChanged(); + } + }, Qt::QueuedConnection); +} + + void ApplicationModel::onStatusChanged(const ReaderManagerPluginInfo& pInfo) { #if defined(QT_NO_DEBUG) || defined(Q_OS_IOS) || defined(Q_OS_ANDROID) || defined(Q_OS_WINRT) @@ -62,7 +74,7 @@ , mFeedback() , mFeedbackTimer() , mIsAppInForeground(true) -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(Q_OS_MACOS) , mPrivate(new Private()) #endif { @@ -81,19 +93,14 @@ onApplicationStateChanged(QGuiApplication::applicationState()); connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &ApplicationModel::onApplicationStateChanged); + connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &ApplicationModel::fireAppAboutToQuit); } void ApplicationModel::resetContext(const QSharedPointer& pContext) { - if (mContext) - { - disconnect(mContext.data(), &WorkflowContext::fireReaderPluginTypesChanged, this, &ApplicationModel::fireAvailableReaderChanged); - } - if ((mContext = pContext)) { - connect(mContext.data(), &WorkflowContext::fireReaderPluginTypesChanged, this, &ApplicationModel::fireAvailableReaderChanged); connect(mContext.data(), &WorkflowContext::fireReaderNameChanged, this, &ApplicationModel::fireAvailableReaderChanged); connect(mContext.data(), &WorkflowContext::fireReaderNameChanged, this, &ApplicationModel::fireReaderPropertiesUpdated); connect(mContext.data(), &WorkflowContext::fireReaderInfoChanged, this, &ApplicationModel::fireReaderPropertiesUpdated); @@ -104,8 +111,8 @@ int ApplicationModel::randomInt(int pLowerBound, int pUpperBound) const { - std::uniform_int_distribution distribution(pLowerBound, pUpperBound); - return distribution(Randomizer::getInstance().getGenerator()); + std::uniform_int_distribution distribution(pLowerBound, pUpperBound); + return distribution(*Randomizer::getInstance().getGenerator()); } @@ -130,14 +137,6 @@ } -QUrl ApplicationModel::getReleaseNotesUrl() const -{ - const auto* storage = Env::getSingleton(); - const auto& url = VersionNumber::getApplicationVersion().isBetaVersion() ? storage->getAppcastBetaUpdateUrl() : storage->getAppcastUpdateUrl(); - return url.adjusted(QUrl::RemoveFilename).toString() + QStringLiteral("ReleaseNotes.html"); -} - - ApplicationModel::NfcState ApplicationModel::getNfcState() const { #if defined(QT_NO_DEBUG) || defined(Q_OS_IOS) || defined(Q_OS_ANDROID) || defined(Q_OS_WINRT) @@ -232,41 +231,42 @@ } -qsizetype ApplicationModel::getAvailableReader() const +qsizetype ApplicationModel::getAvailablePcscReader() const { if (!mContext) { return 0; } - return Env::getSingleton()->getReaderInfos(ReaderFilter(mContext->getReaderPluginTypes())).size(); + return Env::getSingleton()->getReaderInfos(ReaderFilter({ReaderManagerPluginType::PCSC})).size(); } -qsizetype ApplicationModel::getAvailablePcscReader() const +qsizetype ApplicationModel::getAvailableRemoteReader() const { if (!mContext) { return 0; } - return Env::getSingleton()->getReaderInfos(ReaderFilter({ReaderManagerPluginType::PCSC})).size(); + return Env::getSingleton()->getReaderInfos(ReaderFilter({ReaderManagerPluginType::REMOTE_IFD})).size(); } -bool ApplicationModel::isReaderTypeAvailable(ReaderManagerPluginType pPluginType) const +ReaderManagerPluginType ApplicationModel::getUsedPluginType() const { if (!mContext) { - return false; + return ReaderManagerPluginType::UNKNOWN; } - if (!mContext->getReaderName().isEmpty()) + const auto& readerName = mContext->getReaderName(); + if (readerName.isEmpty()) { - return Env::getSingleton()->getReaderInfo(mContext->getReaderName()).getPluginType() == pPluginType; + return ReaderManagerPluginType::UNKNOWN; } - return !Env::getSingleton()->getReaderInfos(ReaderFilter({pPluginType})).isEmpty(); + return Env::getSingleton()->getReaderInfo(readerName).getPluginType(); } @@ -356,7 +356,7 @@ #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) QUrl ApplicationModel::getCustomConfigPath() const { - return Env::getSingleton()->getCustomConfigPath(); + return QUrl(Env::getSingleton()->getCustomConfigPath()); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/ApplicationModel.h ausweisapp2-2.4.0/src/ui/qml/ApplicationModel.h --- ausweisapp2-2.3.1/src/ui/qml/ApplicationModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/ApplicationModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -12,13 +12,14 @@ #include #include +#include #include #include #include #include -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(Q_OS_MACOS) Q_FORWARD_DECLARE_OBJC_CLASS(VoiceOverObserver); #endif @@ -43,25 +44,24 @@ friend class ::test_ApplicationModel; Q_PROPERTY(QString storeUrl READ getStoreUrl NOTIFY fireStoreUrlChanged) - Q_PROPERTY(QUrl releaseNotesUrl READ getReleaseNotesUrl CONSTANT) Q_PROPERTY(NfcState nfcState READ getNfcState NOTIFY fireNfcStateChanged) Q_PROPERTY(bool extendedLengthApdusUnsupported READ isExtendedLengthApdusUnsupported NOTIFY fireReaderPropertiesUpdated) - Q_PROPERTY(bool isSmartSupported READ isSmartSupported CONSTANT) + Q_PROPERTY(bool smartSupported READ isSmartSupported CONSTANT) Q_PROPERTY(bool wifiEnabled READ isWifiEnabled NOTIFY fireWifiEnabledChanged) Q_PROPERTY(Workflow currentWorkflow READ getCurrentWorkflow NOTIFY fireCurrentWorkflowChanged) - // QT_VERSION_CHECK(6, 8, 0) qint64 to qsizetype - Q_PROPERTY(qint64 availableReader READ getAvailableReader NOTIFY fireAvailableReaderChanged) - Q_PROPERTY(qint64 availablePcscReader READ getAvailablePcscReader NOTIFY fireAvailableReaderChanged) + Q_PROPERTY(qsizetype availablePcscReader READ getAvailablePcscReader NOTIFY fireAvailableReaderChanged) + Q_PROPERTY(qsizetype availableRemoteReader READ getAvailableRemoteReader NOTIFY fireAvailableReaderChanged) + Q_PROPERTY(governikus::EnumReaderManagerPluginType::ReaderManagerPluginType usedPluginType READ getUsedPluginType NOTIFY fireAvailableReaderChanged) Q_PROPERTY(QString feedback READ getFeedback NOTIFY fireFeedbackChanged) Q_PROPERTY(int feedbackTimeout READ getFeedbackTimeout CONSTANT) - Q_PROPERTY(bool isScreenReaderRunning READ isScreenReaderRunning NOTIFY fireScreenReaderRunningChanged) + Q_PROPERTY(bool screenReaderRunning READ isScreenReaderRunning NOTIFY fireScreenReaderRunningChanged) #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) Q_PROPERTY(QUrl customConfigPath READ getCustomConfigPath CONSTANT) @@ -74,7 +74,7 @@ QStringList mFeedback; QTimer mFeedbackTimer; bool mIsAppInForeground; -#ifdef Q_OS_IOS +#if defined(Q_OS_IOS) || defined(Q_OS_MACOS) struct Private { Private(); @@ -135,7 +135,6 @@ [[nodiscard]] Q_INVOKABLE int randomInt(int pLowerBound, int pUpperBound) const; [[nodiscard]] QString getStoreUrl() const; - [[nodiscard]] QUrl getReleaseNotesUrl() const; [[nodiscard]] NfcState getNfcState() const; [[nodiscard]] bool isExtendedLengthApdusUnsupported() const; @@ -144,14 +143,14 @@ [[nodiscard]] bool isWifiEnabled() const; [[nodiscard]] Workflow getCurrentWorkflow() const; - [[nodiscard]] qsizetype getAvailableReader() const; [[nodiscard]] qsizetype getAvailablePcscReader() const; + [[nodiscard]] qsizetype getAvailableRemoteReader() const; [[nodiscard]] QString getFeedback() const; [[nodiscard]] bool isScreenReaderRunning() const; - [[nodiscard]] Q_INVOKABLE bool isReaderTypeAvailable(ReaderManagerPluginType pPluginType) const; + [[nodiscard]] ReaderManagerPluginType getUsedPluginType() const; Q_INVOKABLE void enableWifi()const; @@ -167,11 +166,14 @@ #endif [[nodiscard]] Q_INVOKABLE QString stripHtmlTags(QString pString) const; Q_INVOKABLE void showAppStoreRatingDialog() const; + static void notifyScreenReaderChangedThreadSafe(); public Q_SLOTS: Q_INVOKABLE void onShowNextFeedback(); Q_SIGNALS: + void fireAppAboutToQuit(); + void fireStoreUrlChanged(); void fireNfcStateChanged(); @@ -187,6 +189,8 @@ void fireApplicationStateChanged(bool pIsAppInForeground); void fireScreenReaderRunningChanged(); + + void fireA11yFocusChanged(QQuickItem* pItem); }; diff -Nru ausweisapp2-2.3.1/src/ui/qml/ApplicationModel_android.cpp ausweisapp2-2.4.0/src/ui/qml/ApplicationModel_android.cpp --- ausweisapp2-2.3.1/src/ui/qml/ApplicationModel_android.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/ApplicationModel_android.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -20,13 +20,7 @@ { Q_UNUSED(pEnv) Q_UNUSED(pObj) - QMetaObject::invokeMethod(QCoreApplication::instance(), [] { - auto* applicationModel = Env::getSingleton(); - if (applicationModel) - { - Q_EMIT applicationModel->fireScreenReaderRunningChanged(); - } - }, Qt::QueuedConnection); + ApplicationModel::notifyScreenReaderChangedThreadSafe(); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/ApplicationModel_generic.cpp ausweisapp2-2.4.0/src/ui/qml/ApplicationModel_generic.cpp --- ausweisapp2-2.3.1/src/ui/qml/ApplicationModel_generic.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/ApplicationModel_generic.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -18,6 +18,5 @@ bool ApplicationModel::isScreenReaderRunning() const { - qCWarning(qml) << "NOT IMPLEMENTED"; return false; } diff -Nru ausweisapp2-2.3.1/src/ui/qml/ApplicationModel_ios.mm ausweisapp2-2.4.0/src/ui/qml/ApplicationModel_ios.mm --- ausweisapp2-2.3.1/src/ui/qml/ApplicationModel_ios.mm 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/ApplicationModel_ios.mm 2025-10-30 10:10:48.000000000 +0000 @@ -5,6 +5,7 @@ #include "ApplicationModel.h" #include "PlatformTools.h" +#include #include #import @@ -15,6 +16,13 @@ using namespace governikus; + +@interface QMacAccessibilityElement + : NSObject +@property (readonly) QAccessible::Id axid; +@end + + @interface VoiceOverObserver : NSObject @property BOOL mRunning; @@ -38,6 +46,12 @@ name:UIAccessibilityVoiceOverStatusDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(receiveNotification:) + name:UIAccessibilityElementFocusedNotification + object:nil]; + self.mRunning = UIAccessibilityIsVoiceOverRunning(); return self; @@ -53,11 +67,35 @@ if (self.mRunning != isRunning) { self.mRunning = isRunning; - QMetaObject::invokeMethod(QCoreApplication::instance(), [] { + ApplicationModel::notifyScreenReaderChangedThreadSafe(); + } + } + else if ([notification.name + isEqualToString: + UIAccessibilityElementFocusedNotification]) + { + id element = notification.userInfo[UIAccessibilityFocusedElementKey]; + QMacAccessibilityElement* a11yElement = static_cast(element); + if (!a11yElement) + { + return; + } + if (![a11yElement respondsToSelector:@selector(axid)]) + { + return; + } + QAccessibleInterface* iface = QAccessible::accessibleInterface(a11yElement.axid); + if (!iface || !iface->object()) + { + return; + } + if (auto* quickItem = static_cast(iface->object())) + { + QMetaObject::invokeMethod(QCoreApplication::instance(), [quickItem] { auto* applicationModel = Env::getSingleton(); if (applicationModel) { - Q_EMIT applicationModel->fireScreenReaderRunningChanged(); + Q_EMIT applicationModel->fireA11yFocusChanged(quickItem); } }, Qt::QueuedConnection); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/ApplicationModel_osx.mm ausweisapp2-2.4.0/src/ui/qml/ApplicationModel_osx.mm --- ausweisapp2-2.3.1/src/ui/qml/ApplicationModel_osx.mm 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/ApplicationModel_osx.mm 2025-10-30 10:10:48.000000000 +0000 @@ -5,16 +5,79 @@ #include "ApplicationModel.h" #include +#include using namespace governikus; Q_DECLARE_LOGGING_CATEGORY(qml) +@interface VoiceOverObserver + : NSObject +@property BOOL mRunning; +- (instancetype) init; +- (void) observeValueForKeyPath:(NSString*) keyPath + ofObject:(id) object + change:(NSDictionary*) change + context:(void*) context; +@end + +@implementation VoiceOverObserver + + +- (instancetype)init { + self = [super init]; + if (!self) + { + return nil; + } + + [[NSWorkspace sharedWorkspace] + addObserver:self + forKeyPath:@"voiceOverEnabled" + options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) + context:nil]; + + self.mRunning = NSWorkspace.sharedWorkspace.voiceOverEnabled; + + return self; +} + + +- (void) observeValueForKeyPath:(NSString*) keyPath + ofObject:(id) object + change:(NSDictionary*) change + context:(void*) context { + if ([keyPath isEqualToString:@"voiceOverEnabled"]) + { + BOOL isRunning = NSWorkspace.sharedWorkspace.voiceOverEnabled; + if (self.mRunning != isRunning) + { + self.mRunning = isRunning; + ApplicationModel::notifyScreenReaderChangedThreadSafe(); + } + return; + } +} + + +@end + + +ApplicationModel::Private::Private() : mObserver([[VoiceOverObserver alloc] init]) +{ +} + + +// It's important that the definition of the destructor is in a .mm file: Otherwise the compiler won't compile it in Objective-C++ mode and ARC won't work. +ApplicationModel::Private::~Private() +{ +} + bool ApplicationModel::isScreenReaderRunning() const { - return NSWorkspace.sharedWorkspace.voiceOverEnabled; + return mPrivate->mObserver.mRunning; } diff -Nru ausweisapp2-2.3.1/src/ui/qml/AuthModel.cpp ausweisapp2-2.4.0/src/ui/qml/AuthModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/AuthModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/AuthModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -25,6 +25,7 @@ connect(mContext.data(), &AuthContext::fireShowChangePinViewChanged, this, &AuthModel::fireChangeTransportPinChanged); connect(mContext.data(), &AuthContext::fireDidAuthenticateEac1Changed, this, &AuthModel::onDidAuthenticateEac1Changed); connect(mContext.data(), &AuthContext::fireProgressChanged, this, &AuthModel::fireProgressChanged); + connect(mContext.data(), &AuthContext::fireRefreshUrlChanged, this, &AuthModel::fireRefreshUrlChanged); } if (!mTransactionInfo.isEmpty()) @@ -109,6 +110,12 @@ return QString(); } + if (getStatusCode() == GlobalStatus::Code::Workflow_Card_Removed) + { + //: LABEL ALL_PLATFORMS + return tr("Connection to ID card lost"); + } + const auto& tcTokenUrl = mContext->getTcTokenUrl(); return tcTokenUrl.scheme() + QStringLiteral("://") + tcTokenUrl.authority(); } @@ -141,12 +148,6 @@ } -QString AuthModel::getStatusCodeString() const -{ - return getEnumName(getStatusCode()); -} - - QString AuthModel::getResultViewButtonIcon() const { if (!mContext) @@ -214,3 +215,13 @@ } } } + + +QUrl AuthModel::getRefreshUrl() const +{ + if (mContext) + { + return mContext->getRefreshUrl(); + } + return QUrl(); +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/AuthModel.h ausweisapp2-2.4.0/src/ui/qml/AuthModel.h --- ausweisapp2-2.3.1/src/ui/qml/AuthModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/AuthModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -40,10 +40,10 @@ Q_PROPERTY(QString resultHeader READ getResultHeader NOTIFY fireResultChanged) Q_PROPERTY(QString errorHeader READ getErrorHeader NOTIFY fireResultChanged) Q_PROPERTY(QString errorText READ getErrorText NOTIFY fireResultChanged) - Q_PROPERTY(QString statusCodeString READ getStatusCodeString NOTIFY fireResultChanged) Q_PROPERTY(QString resultViewButtonIcon READ getResultViewButtonIcon NOTIFY fireResultChanged) Q_PROPERTY(QString resultViewButtonText READ getResultViewButtonText NOTIFY fireResultChanged) Q_PROPERTY(QUrl resultViewButtonLink READ getResultViewButtonLink NOTIFY fireResultChanged) + Q_PROPERTY(QUrl refreshUrl READ getRefreshUrl NOTIFY fireRefreshUrlChanged) private: QSharedPointer mContext; @@ -63,10 +63,10 @@ [[nodiscard]] QString getResultHeader() const; [[nodiscard]] QString getErrorHeader() const; [[nodiscard]] QString getErrorText() const; - [[nodiscard]] QString getStatusCodeString() const; [[nodiscard]] QString getResultViewButtonIcon() const; [[nodiscard]] QString getResultViewButtonText() const; [[nodiscard]] QUrl getResultViewButtonLink() const; + [[nodiscard]] QUrl getRefreshUrl() const; Q_INVOKABLE void cancelWorkflowToChangeTransportPin(); @@ -77,6 +77,7 @@ void fireChangeTransportPinChanged(); void fireTransactionInfoChanged(); void fireProgressChanged(); + void fireRefreshUrlChanged(); }; diff -Nru ausweisapp2-2.3.1/src/ui/qml/CMakeLists.txt ausweisapp2-2.4.0/src/ui/qml/CMakeLists.txt --- ausweisapp2-2.3.1/src/ui/qml/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -9,7 +9,9 @@ target_link_libraries(AusweisAppUiQml PUBLIC ${Qt}::Core ${Qt}::Svg ${Qt}::Qml ${Qt}::Quick ${Qt}::QuickControls2) target_link_libraries(AusweisAppUiQml PUBLIC AusweisAppGlobal AusweisAppUi AusweisAppIfdRemote AusweisAppServices AusweisAppWorkflowsSelfAuth AusweisAppUiQmlModules) -target_link_libraries(AusweisAppUiQml PUBLIC ${Qt}::CorePrivate) +if(ANDROID) + target_link_libraries(AusweisAppUiQml PUBLIC ${Qt}::CorePrivate) +endif() if(TARGET ${Qt}::Widgets) target_link_libraries(AusweisAppUiQml PUBLIC ${Qt}::Widgets) # QSystemTrayIcon @@ -57,28 +59,9 @@ target_link_libraries(AusweisAppUiQml INTERFACE AusweisAppUiQmlplugin) endif() -if(QT_VERSION VERSION_GREATER_EQUAL "6.7.1" - OR (QT_VERSION VERSION_GREATER_EQUAL "6.5.6" AND QT_VERSION VERSION_LESS "6.6") - ) - if(TARGET AusweisAppDiagnosis) - qt_generate_foreign_qml_types(AusweisAppDiagnosis AusweisAppUiQml) - endif() - qt_generate_foreign_qml_types(AusweisAppGlobal AusweisAppUiQml) - qt_generate_foreign_qml_types(AusweisAppUi AusweisAppUiQml) - qt_generate_foreign_qml_types(AusweisAppCard AusweisAppUiQml) -else() - set_property(SOURCE UiPluginQml.cpp PROPERTY COMPILE_DEFINITIONS USE_CUSTOM_REGISTRATION) -endif() - -if(QT_VERSION VERSION_LESS "6.5") - set(COMPONENT_BEHAVIOR "pragma ComponentBehavior: Bound") - file(GLOB_RECURSE INCOMPATIBLE_QML_FILES "modules/*.qml") - foreach(ABSOLUTE_FILE_PATH ${INCOMPATIBLE_QML_FILES}) - file(READ "${ABSOLUTE_FILE_PATH}" FILE_CONTENT) - if (FILE_CONTENT MATCHES ${COMPONENT_BEHAVIOR}) - string(REPLACE "${COMPONENT_BEHAVIOR}" "" FILE_CONTENT "${FILE_CONTENT}") - message(STATUS "Removed \"${COMPONENT_BEHAVIOR}\" from ${ABSOLUTE_FILE_PATH} because of QTBUG-108684") - file(WRITE "${ABSOLUTE_FILE_PATH}" "${FILE_CONTENT}") - endif() - endforeach() +if(TARGET AusweisAppDiagnosis) + qt_generate_foreign_qml_types(AusweisAppDiagnosis AusweisAppUiQml) endif() +qt_generate_foreign_qml_types(AusweisAppGlobal AusweisAppUiQml) +qt_generate_foreign_qml_types(AusweisAppUi AusweisAppUiQml) +qt_generate_foreign_qml_types(AusweisAppCard AusweisAppUiQml) diff -Nru ausweisapp2-2.3.1/src/ui/qml/CardPosition.cpp ausweisapp2-2.4.0/src/ui/qml/CardPosition.cpp --- ausweisapp2-2.3.1/src/ui/qml/CardPosition.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/CardPosition.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,38 +4,53 @@ #include "CardPosition.h" + using namespace governikus; -CardPosition::CardPosition(double pXPosition, double pYPosition, int pZPosition, double pRotation) + +CardPosition::CardPosition(const Data& pData) : QObject() - , mXPosition(pXPosition) - , mYPosition(pYPosition) - , mZPosition(pZPosition) - , mRotation(pRotation) + , mData(pData) { - Q_ASSERT(mXPosition >= 0.0 && mXPosition <= 1.0); - Q_ASSERT(mYPosition >= 0.0 && mYPosition <= 1.0); - Q_ASSERT(mZPosition == -1 || mZPosition == 1); - Q_ASSERT(mRotation >= 0 && mRotation <= 360); + Q_ASSERT(mData.mXPosition >= 0.0 && mData.mXPosition <= 1.0); + Q_ASSERT(mData.mYPosition >= 0.0 && mData.mYPosition <= 1.0); + Q_ASSERT(mData.mRotation >= 0.0 && mData.mRotation <= 360.0); + Q_ASSERT(mData.mZPosition == -1 || mData.mZPosition == 1); } -CardPosition::CardPosition(const CardPosition& pOther) - : QObject() - , mXPosition(pOther.mXPosition) - , mYPosition(pOther.mYPosition) - , mZPosition(pOther.mZPosition) - , mRotation(pOther.mRotation) +double CardPosition::getXPosition() const +{ + return mData.mXPosition; +} + + +double CardPosition::getYPosition() const +{ + return mData.mYPosition; +} + + +double CardPosition::getRotation() const { + return mData.mRotation; +} + +int CardPosition::getZPosition() const +{ + return mData.mZPosition; } -CardPosition& CardPosition::operator=(const CardPosition& pOther) +#ifndef QT_NO_DEBUG +bool CardPosition::operator==(const CardPosition& pOther) const { - mXPosition = pOther.mXPosition; - mYPosition = pOther.mYPosition; - mZPosition = pOther.mZPosition; - mRotation = pOther.mRotation; - return *this; + return mData.mXPosition == pOther.mData.mXPosition + && mData.mYPosition == pOther.mData.mYPosition + && mData.mRotation == pOther.mData.mRotation + && mData.mZPosition == pOther.mData.mZPosition; } + + +#endif diff -Nru ausweisapp2-2.3.1/src/ui/qml/CardPosition.h ausweisapp2-2.4.0/src/ui/qml/CardPosition.h --- ausweisapp2-2.3.1/src/ui/qml/CardPosition.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/CardPosition.h 2025-10-30 10:10:48.000000000 +0000 @@ -7,6 +7,7 @@ #include #include + namespace governikus { @@ -16,21 +17,47 @@ Q_OBJECT QML_ELEMENT - Q_PROPERTY(double x MEMBER mXPosition CONSTANT) - Q_PROPERTY(double y MEMBER mYPosition CONSTANT) - Q_PROPERTY(int z MEMBER mZPosition CONSTANT) - Q_PROPERTY(double rotation MEMBER mRotation CONSTANT) + private: + Q_PROPERTY(double x READ getXPosition CONSTANT) + Q_PROPERTY(double y READ getYPosition CONSTANT) + Q_PROPERTY(double rotation READ getRotation CONSTANT) + Q_PROPERTY(int z READ getZPosition CONSTANT) + + public: + struct Data + { + double mXPosition; + double mYPosition; + double mRotation = 0; + int mZPosition = -1; + }; + + private: + Data mData; public: - CardPosition(double pXPosition = 0.0, double pYPosition = 0.0, int pZPosition = 1, double pRotation = 0.0); - CardPosition(const CardPosition& pOther); + explicit CardPosition(const Data& pData = {0.0, 0.0, 0.0, -1}); ~CardPosition() override = default; - CardPosition& operator=(const CardPosition& pOther); - double mXPosition; - double mYPosition; - int mZPosition; - double mRotation; + [[nodiscard]] double getXPosition() const; + [[nodiscard]] double getYPosition() const; + [[nodiscard]] double getRotation() const; + [[nodiscard]] int getZPosition() const; + +#ifndef QT_NO_DEBUG + bool operator==(const CardPosition& pOther) const; + + #if __cplusplus < 202002L + inline bool operator!=(const CardPosition& pOther) + { + return !(*this == pOther); + } + + + #endif +#endif + + }; diff -Nru ausweisapp2-2.3.1/src/ui/qml/CardPositionModel.cpp ausweisapp2-2.4.0/src/ui/qml/CardPositionModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/CardPositionModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/CardPositionModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,22 +6,25 @@ #include + using namespace governikus; + CardPositionModel::CardPositionModel() : QObject() , mCyclingClock(5000) , mCurrentIndex(0) + , mCardPositions({ #if defined(Q_OS_IOS) - , mCardPositions({CardPosition(0.5, 0.0, -1)}) + {0.5, 0.0} + #else - , mCardPositions({ - CardPosition(0.5, 0.5, -1, 90), - CardPosition(0.5, 0.5, -1), - CardPosition(0.5, 0.25, -1), - CardPosition(0.5, 0.75, -1) - }) + {0.5, 0.5, 90}, + {0.5, 0.5}, + {0.5, 0.25}, + {0.5, 0.75} #endif + }) { connect(&mCyclingTimer, &QTimer::timeout, this, &CardPositionModel::onTimerTimeout); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/CardPositionModel.h ausweisapp2-2.4.0/src/ui/qml/CardPositionModel.h --- ausweisapp2-2.3.1/src/ui/qml/CardPositionModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/CardPositionModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -11,8 +11,10 @@ #include #include + class test_CardPositionModel; + namespace governikus { @@ -32,7 +34,7 @@ int mCyclingClock; qsizetype mCurrentIndex; QTimer mCyclingTimer; - const QList mCardPositions; + const QList mCardPositions; void startTimer(); void stopTimer(); diff -Nru ausweisapp2-2.3.1/src/ui/qml/CertificateDescriptionModel.cpp ausweisapp2-2.4.0/src/ui/qml/CertificateDescriptionModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/CertificateDescriptionModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/CertificateDescriptionModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -66,28 +66,28 @@ const bool showDetailedProviderInfo = !(serviceProviderAddress.isEmpty() || purpose.isEmpty() || dataSecurityOfficer.isEmpty()); //: LABEL ALL_PLATFORMS - mData += QPair(tr("Provider"), getSubjectName() + QLatin1Char('\n') + getSubjectUrl()); + mData += std::make_pair(tr("Provider"), getSubjectName() + QLatin1Char('\n') + getSubjectUrl()); //: LABEL ALL_PLATFORMS - mData += QPair(tr("Certificate issuer"), pCertDescription->getIssuerName() + QLatin1Char('\n') + pCertDescription->getIssuerUrl()); + mData += std::make_pair(tr("Certificate issuer"), pCertDescription->getIssuerName() + QLatin1Char('\n') + pCertDescription->getIssuerUrl()); if (showDetailedProviderInfo) { //: LABEL ALL_PLATFORMS - mData += QPair(tr("Name, address and mail address of the provider"), serviceProviderAddress); + mData += std::make_pair(tr("Name, address and mail address of the provider"), serviceProviderAddress); //: LABEL ALL_PLATFORMS - mData += QPair(tr("Purpose"), purpose); + mData += std::make_pair(tr("Purpose"), purpose); //: LABEL ALL_PLATFORMS - mData += QPair(tr("Indication of the bodies responsible for the provider, " - "that verify the compliance with data security regulations"), dataSecurityOfficer); + mData += std::make_pair(tr("Indication of the bodies responsible for the provider, " + "that verify the compliance with data security regulations"), dataSecurityOfficer); } else if (!termsOfUsage.isEmpty()) { //: LABEL ALL_PLATFORMS - mData += QPair(tr("Provider information"), termsOfUsage); + mData += std::make_pair(tr("Provider Information"), termsOfUsage); } if (!getValidity().isEmpty()) { //: LABEL ALL_PLATFORMS - mData += QPair(tr("Validity"), getValidity()); + mData += std::make_pair(tr("Validity"), getValidity()); } } @@ -153,7 +153,7 @@ QVariant CertificateDescriptionModel::data(const QModelIndex& pIndex, int pRole) const { - if (pIndex.isValid() && pIndex.row() < rowCount()) + if (pIndex.isValid() && pIndex.row() < rowCount(pIndex)) { const auto& [label, text] = mData[pIndex.row()]; if (pRole == LABEL) diff -Nru ausweisapp2-2.3.1/src/ui/qml/CertificateDescriptionModel.h ausweisapp2-2.4.0/src/ui/qml/CertificateDescriptionModel.h --- ausweisapp2-2.3.1/src/ui/qml/CertificateDescriptionModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/CertificateDescriptionModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -11,11 +11,11 @@ #include #include -#include #include #include #include +#include class test_UiPluginQml; @@ -38,7 +38,7 @@ Q_PROPERTY(QString purpose READ getPurpose NOTIFY fireChanged) private: - QList> mData; + QList> mData; QSharedPointer mContext; CertificateDescriptionModel(); @@ -67,7 +67,7 @@ [[nodiscard]] QString getSubjectUrl() const; [[nodiscard]] QString getPurpose() const; - [[nodiscard]] int rowCount(const QModelIndex& = QModelIndex()) const override; + [[nodiscard]] int rowCount(const QModelIndex&) const override; [[nodiscard]] QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; [[nodiscard]] QHash roleNames() const override; diff -Nru ausweisapp2-2.3.1/src/ui/qml/ChatModel.cpp ausweisapp2-2.4.0/src/ui/qml/ChatModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/ChatModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/ChatModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -113,7 +113,7 @@ QVariant ChatModel::data(const QModelIndex& pIndex, int pRole) const { - if (!pIndex.isValid() || pIndex.row() >= rowCount()) + if (!pIndex.isValid() || pIndex.row() >= rowCount(pIndex)) { return QVariant(); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/ChatModel.h ausweisapp2-2.4.0/src/ui/qml/ChatModel.h --- ausweisapp2-2.3.1/src/ui/qml/ChatModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/ChatModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -72,7 +72,7 @@ public: void resetContext(const QSharedPointer& pContext = QSharedPointer()); - [[nodiscard]] int rowCount(const QModelIndex& = QModelIndex()) const override; + [[nodiscard]] int rowCount(const QModelIndex&) const override; [[nodiscard]] QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; [[nodiscard]] bool setData(const QModelIndex& pIndex, const QVariant& pValue, int pRole) override; [[nodiscard]] QHash roleNames() const override; diff -Nru ausweisapp2-2.3.1/src/ui/qml/FormattedTextModel.cpp ausweisapp2-2.4.0/src/ui/qml/FormattedTextModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/FormattedTextModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/FormattedTextModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -36,7 +36,7 @@ QVariant FormattedTextModel::data(const QModelIndex& pIndex, int pRole) const { const auto row = pIndex.row(); - if (!pIndex.isValid() || row >= rowCount()) + if (!pIndex.isValid() || row >= rowCount(pIndex)) { return QVariant(); } @@ -93,7 +93,7 @@ bool FormattedTextModel::isFormattingLine(FormattedTextModel::LineType pType) { - static const QList formattingLineTypes({LineType::HEADER, LineType::SECTION, LineType::SUBSECTION}); + static const QList formattingLineTypes({LineType::HEADER, LineType::SECTION, LineType::SUBSECTION}); return formattingLineTypes.contains(pType); } @@ -196,7 +196,7 @@ if ((lastLineIsEmpty() && !isFormattingLine(type)) || type == LineType::EMPTY) { - mLines << qMakePair(htmlLine, type); + mLines << std::make_pair(htmlLine, type); return; } diff -Nru ausweisapp2-2.3.1/src/ui/qml/FormattedTextModel.h ausweisapp2-2.4.0/src/ui/qml/FormattedTextModel.h --- ausweisapp2-2.3.1/src/ui/qml/FormattedTextModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/FormattedTextModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -6,11 +6,12 @@ #include #include -#include #include #include #include +#include + class test_FormattedTextModel; namespace governikus @@ -45,7 +46,7 @@ explicit FormattedTextModel(QObject* pParent, const QStringList& pLines = QStringList()); - [[nodiscard]] int rowCount(const QModelIndex& pIndex = QModelIndex()) const override; + [[nodiscard]] int rowCount(const QModelIndex& pIndex) const override; [[nodiscard]] QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; [[nodiscard]] QHash roleNames() const override; @@ -65,7 +66,7 @@ static ReadLinesResult readLines(const QString& pFilepath); private: - QList> mLines; + QList> mLines; void processLines(const QStringList& pLines); void processLine(const QString& pLine); diff -Nru ausweisapp2-2.3.1/src/ui/qml/LogFilesModel.cpp ausweisapp2-2.4.0/src/ui/qml/LogFilesModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/LogFilesModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/LogFilesModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,143 @@ +/** + * Copyright (c) 2018-2025 Governikus GmbH & Co. KG, Germany + */ + +#include "LogFilesModel.h" +#include "LanguageLoader.h" +#include "LogHandler.h" +#include "Randomizer.h" + +#include +#include +#include + + +using namespace governikus; + + +LogFilesModel::LogFilesModel() + : QAbstractListModel() + , mLogFiles() +{ + reset(); +} + + +void LogFilesModel::reset() +{ + beginResetModel(); + + mLogFiles.clear(); + mLogFiles += QString(); // dummy entry for "current logfile" + const auto logFiles = Env::getSingleton()->getOtherLogFiles(); + for (const auto& entry : logFiles) + { + mLogFiles += entry.absoluteFilePath(); + } + + endResetModel(); + Q_EMIT fireCountChanged(); +} + + +void LogFilesModel::removeOtherLogFiles() +{ + if (Env::getSingleton()->removeOtherLogFiles()) + { + reset(); + } +} + + +void LogFilesModel::saveDummyLogFile(const QDateTime& pTimestamp) +{ +#ifdef QT_NO_DEBUG + Q_UNUSED(pTimestamp) +#else + auto* generator = Randomizer::getInstance().getGenerator(false); + std::uniform_int_distribution dist; + const auto* logHandler = Env::getSingleton(); + const auto& copyFilename = QDir::temp().filePath(QStringLiteral("%1.%2.log").arg(QCoreApplication::applicationName()).arg(dist(*generator))); + if (logHandler->copy(copyFilename) && pTimestamp.isValid()) + { + if (QFile file(copyFilename); file.open(QFile::Append)) + { + file.setFileTime(pTimestamp, QFile::FileModificationTime); + } + } + reset(); +#endif +} + + +QString LogFilesModel::getLogFileName(int pIndex) const +{ + return data(index(pIndex, 0), NameRole).toString(); +} + + +QString LogFilesModel::getLogFilePath(int pIndex) const +{ + return data(index(pIndex, 0), PathRole).toString(); +} + + +int LogFilesModel::getCount() const +{ + return rowCount(QModelIndex()); +} + + +int LogFilesModel::rowCount(const QModelIndex& pIndex) const +{ + Q_UNUSED(pIndex) + return static_cast(mLogFiles.size()); +} + + +QHash LogFilesModel::roleNames() const +{ + QHash roles; + roles.insert(Qt::DisplayRole, QByteArrayLiteral("modelData")); + roles.insert(NameRole, QByteArrayLiteral("name")); + roles.insert(PathRole, QByteArrayLiteral("path")); + return roles; +} + + +QVariant LogFilesModel::data(const QModelIndex& pIndex, int pRole) const +{ + if (!pIndex.isValid()) + { + return QVariant(); + } + + const auto& file = mLogFiles.at(pIndex.row()); + + switch (pRole) + { + case Qt::DisplayRole: + case NameRole: + { + if (file.isEmpty()) + { + //: LABEL ALL_PLATFORMS + return tr("Current log"); + } + + //: LABEL ALL_PLATFORMS Datetime format according to https://doc.qt.io/qt/qdate.html#toString and https://doc.qt.io/qt/qtime.html#toString + const auto fileName = LanguageLoader::getInstance().getUsedLocale().toString(LogHandler::getFileDate(QFileInfo(file)), tr("dd.MM.yyyy hh:mm:ss")); + return fileName; + } + + case PathRole: + { + return mLogFiles.at(pIndex.row()); + } + + default: + return QVariant(); + } + + Q_UNREACHABLE(); +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/LogFilesModel.h ausweisapp2-2.4.0/src/ui/qml/LogFilesModel.h --- ausweisapp2-2.3.1/src/ui/qml/LogFilesModel.h 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/LogFilesModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2018-2025 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include "Env.h" +#include "SingletonCreator.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +class test_LogFilesModel; + +namespace governikus +{ + +class LogFilesModel + : public QAbstractListModel + , public SingletonCreator +{ + Q_OBJECT + QML_ELEMENT + QML_SINGLETON + + friend class Env; + friend class ::test_LogFilesModel; + + Q_PROPERTY(int count READ getCount NOTIFY fireCountChanged) + + private: + QStringList mLogFiles; + + void reset(); + + public: + enum LogFilesModelRoles + { + NameRole = Qt::UserRole + 1, + PathRole + }; + + LogFilesModel(); + ~LogFilesModel() override = default; + + Q_INVOKABLE void removeOtherLogFiles(); + Q_INVOKABLE void saveDummyLogFile(const QDateTime& pTimeStamp = QDateTime()); + [[nodiscard]] Q_INVOKABLE QString getLogFileName(int pIndex) const; + [[nodiscard]] Q_INVOKABLE QString getLogFilePath(int pIndex) const; + [[nodiscard]] int getCount() const; + + int rowCount(const QModelIndex& parent) const override; + QHash roleNames() const override; + QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; + + Q_SIGNALS: + void fireCountChanged(); +}; + + +} // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ui/qml/LogFilterModel.cpp ausweisapp2-2.4.0/src/ui/qml/LogFilterModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/LogFilterModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/LogFilterModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -3,25 +3,40 @@ */ #include "LogFilterModel.h" - #include "LogModel.h" - using namespace governikus; - void LogFilterModel::onLevelsChanged() { - mSelectedLevels.intersect(Env::getSingleton()->getLevels()); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)) + beginFilterChange(); +#endif + + mSelectedLevels.intersect(mSourceModel->getLevels()); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)) + endFilterChange(); +#else invalidateFilter(); +#endif Q_EMIT fireLevelsChanged(); } void LogFilterModel::onCategoriesChanged() { - mSelectedCategories.intersect(Env::getSingleton()->getCategories()); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)) + beginFilterChange(); +#endif + + mSelectedCategories.intersect(mSourceModel->getCategories()); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)) + endFilterChange(); +#else invalidateFilter(); +#endif Q_EMIT fireCategoriesChanged(); } @@ -53,17 +68,20 @@ : QSortFilterProxyModel() , mSelectedLevels() , mSelectedCategories() + , mSourceModel(nullptr) { - const auto& logModel = Env::getSingleton(); - connect(logModel, &LogModel::fireLevelsChanged, this, &LogFilterModel::onLevelsChanged); - connect(logModel, &LogModel::fireCategoriesChanged, this, &LogFilterModel::onCategoriesChanged); - QSortFilterProxyModel::setSourceModel(logModel); + } QStringList LogFilterModel::getLevels() const { - const auto& level = Env::getSingleton()->getLevels(); + if (!mSourceModel) + { + return {}; + } + + const auto& level = mSourceModel->getLevels(); QStringList list(level.constBegin(), level.constEnd()); list.sort(Qt::CaseInsensitive); return list; @@ -78,7 +96,12 @@ QStringList LogFilterModel::getCategories() const { - const auto& categories = Env::getSingleton()->getCategories(); + if (!mSourceModel) + { + return {}; + } + + const auto& categories = mSourceModel->getCategories(); QStringList list(categories.constBegin(), categories.constEnd()); list.sort(Qt::CaseInsensitive); return list; @@ -91,8 +114,43 @@ } +void LogFilterModel::setSourceModel(QAbstractItemModel* pSourceModel) +{ + auto* logModel = qobject_cast(pSourceModel); + if (logModel == nullptr) + { + qWarning() << "Source model isn't a LogModel"; + return; + } + + if (mSourceModel == logModel) + { + return; + } + + if (mSourceModel) + { + disconnect(mSourceModel, &LogModel::fireLevelsChanged, this, &LogFilterModel::onLevelsChanged); + disconnect(mSourceModel, &LogModel::fireCategoriesChanged, this, &LogFilterModel::onCategoriesChanged); + } + + mSourceModel = logModel; + QSortFilterProxyModel::setSourceModel(mSourceModel); + + connect(mSourceModel, &LogModel::fireLevelsChanged, this, &LogFilterModel::onLevelsChanged); + connect(mSourceModel, &LogModel::fireCategoriesChanged, this, &LogFilterModel::onCategoriesChanged); + + onLevelsChanged(); + onCategoriesChanged(); +} + + void LogFilterModel::configureLevel(const QString& pLevel, bool pEnabled) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)) + beginFilterChange(); +#endif + if (pEnabled) { mSelectedLevels.insert(pLevel); @@ -102,13 +160,21 @@ mSelectedLevels.remove(pLevel); } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)) + endFilterChange(); +#else invalidateFilter(); +#endif + Q_EMIT fireLevelsChanged(); } void LogFilterModel::configureCategory(const QString& pCategory, bool pEnabled) { +#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)) + beginFilterChange(); +#endif if (pEnabled) { mSelectedCategories.insert(pCategory); @@ -118,6 +184,11 @@ mSelectedCategories.remove(pCategory); } +#if (QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)) + endFilterChange(); +#else invalidateFilter(); +#endif + Q_EMIT fireCategoriesChanged(); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/LogFilterModel.h ausweisapp2-2.4.0/src/ui/qml/LogFilterModel.h --- ausweisapp2-2.3.1/src/ui/qml/LogFilterModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/LogFilterModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -4,6 +4,9 @@ #pragma once +#include "LogModel.h" + +#include #include #include #include @@ -27,6 +30,7 @@ private: QSet mSelectedLevels; QSet mSelectedCategories; + QPointer mSourceModel; private Q_SLOTS: void onLevelsChanged(); @@ -43,6 +47,7 @@ [[nodiscard]] QStringList getSelectedLevels() const; [[nodiscard]] QStringList getCategories() const; [[nodiscard]] QStringList getSelectedCategories() const; + void setSourceModel(QAbstractItemModel* pSourceModel) override; Q_INVOKABLE void configureLevel(const QString& pLevel, bool pEnabled); Q_INVOKABLE void configureCategory(const QString& pCategory, bool pEnabled); @@ -52,4 +57,5 @@ void fireCategoriesChanged(); }; + } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ui/qml/LogModel.cpp ausweisapp2-2.4.0/src/ui/qml/LogModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/LogModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/LogModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,9 +5,7 @@ #include "LogModel.h" #include "ApplicationModel.h" -#include "LanguageLoader.h" #include "LogHandler.h" -#include "Randomizer.h" #include #include @@ -17,32 +15,21 @@ #include +Q_DECLARE_LOGGING_CATEGORY(qml) + + using namespace governikus; LogModel::LogModel() : QAbstractListModel() - , mLogFiles() - , mSelectedLogFile(-1) , mLogEntries() , mLevels() , mCategories() + , mLogFilePath(QStringLiteral("uninitialized")) { - reset(); -} - - -void LogModel::reset() -{ - mLogFiles.clear(); - mLogFiles += QString(); // dummy entry for "current logfile" - const auto logFiles = Env::getSingleton()->getOtherLogFiles(); - for (const auto& entry : logFiles) - { - mLogFiles += entry.absoluteFilePath(); - } - - setLogFile(0); + // initial current log is used + setSource(QString()); } @@ -57,16 +44,16 @@ mLogEntries.append(pEntry); - QModelIndex idx = index(static_cast(mLogEntries.size()) - 1, 0); + QModelIndex idx = QAbstractListModel::index(static_cast(mLogEntries.size()) - 1, 0); - const auto& level = data(idx, LogModel::LogModelRoles::LevelRole).toString(); + const auto& level = LogModel::data(idx, LogModel::LogModelRoles::LevelRole).toString(); if (!mLevels.contains(level)) { mLevels.insert(level); Q_EMIT fireLevelsChanged(); } - const auto& category = data(idx, LogModel::LogModelRoles::CategoryRole).toString(); + const auto& category = LogModel::data(idx, LogModel::LogModelRoles::CategoryRole).toString(); if (!mCategories.contains(category)) { mCategories.insert(category); @@ -98,9 +85,15 @@ } +bool LogModel::isCurrentLog() const +{ + return mLogFilePath.isEmpty(); +} + + void LogModel::onNewLogMsg(const QString& pMsg) { - if (mSelectedLogFile == 0) + if (isCurrentLog()) { const auto size = static_cast(mLogEntries.size()); beginInsertRows(QModelIndex(), size, size); @@ -111,30 +104,6 @@ } -void LogModel::onTranslationChanged() -{ - Q_EMIT fireLogFileNamesChanged(); -} - - -QStringList LogModel::getLogFileNames() const -{ - QStringList logFileNames; - //: LABEL ALL_PLATFORMS - logFileNames += tr("Current log"); - for (const auto& entry : std::as_const(mLogFiles)) - { - if (!entry.isEmpty()) - { - //: LABEL ALL_PLATFORMS Datetime format according to https://doc.qt.io/qt/qdate.html#toString and https://doc.qt.io/qt/qtime.html#toString - logFileNames += LanguageLoader::getInstance().getUsedLocale().toString(LogHandler::getFileDate(QFileInfo(entry)), tr("dd.MM.yyyy hh:mm:ss")); - } - } - - return logFileNames; -} - - const QSet& LogModel::getLevels() const { return mLevels; @@ -147,44 +116,18 @@ } -QDateTime LogModel::getCurrentLogFileDate() const -{ - if (mSelectedLogFile == 0) - { - return Env::getSingleton()->getCurrentLogFileDate(); - } - - return LogHandler::getFileDate(QFileInfo(mLogFiles.at(mSelectedLogFile))); -} - - -void LogModel::removeOtherLogFiles() +void LogModel::setSource(const QString& pLogFilePath) { - if (Env::getSingleton()->removeOtherLogFiles()) + if (mLogFilePath == pLogFilePath) { - reset(); - Q_EMIT fireLogFileNamesChanged(); - } -} - - -void LogModel::setLogFile(int pIndex) -{ - if (pIndex < 0 || pIndex >= mLogFiles.size()) - { - qDebug() << "Called with invalid index:" << pIndex; return; } - if (pIndex == mSelectedLogFile) - { - return; - } + mLogFilePath = pLogFilePath; - mSelectedLogFile = pIndex; auto* logHandler = Env::getSingleton(); - if (pIndex == 0) + if (isCurrentLog()) { QTextStream in(logHandler->useLogFile() ? logHandler->getBacklog() : tr("The logfile is disabled.").toUtf8()); setLogEntries(in); @@ -193,53 +136,64 @@ else { disconnect(logHandler->getEventHandler(), &LogEventHandler::fireLog, this, &LogModel::onNewLogMsg); - QFile inputFile(mLogFiles.value(pIndex)); + QFile inputFile(mLogFilePath); if (inputFile.open(QIODevice::ReadOnly)) { + qCDebug(qml) << "Loaded log:" << pLogFilePath; QTextStream in(&inputFile); setLogEntries(in); inputFile.close(); } + else + { + qCWarning(qml) << "Load file failed:" << pLogFilePath; + QString emptyString; + QTextStream stream(&emptyString); + setLogEntries(stream); + } } + + Q_EMIT fireSourceChanged(); +} + + +[[nodiscard]] QString LogModel::getSource() const +{ + return mLogFilePath; } -void LogModel::saveCurrentLogFile(const QUrl& pFilename) const +bool LogModel::saveLogFile(const QUrl& pFilename, bool pShowFeedback) const { bool success = false; - if (const auto& logfilePath = mLogFiles.at(mSelectedLogFile); logfilePath.isEmpty()) + if (isCurrentLog()) { success = Env::getSingleton()->copy(pFilename.toLocalFile()); } else { - success = Env::getSingleton()->copyOther(logfilePath, pFilename.toLocalFile()); + success = Env::getSingleton()->copyOther(mLogFilePath, pFilename.toLocalFile()); } - auto* applicationModel = Env::getSingleton(); - applicationModel->showFeedback((success ? tr("Successfully saved logfile to \"%1\"") : tr("Error while saving logfile to \"%1\"")).arg(pFilename.toLocalFile())); + if (pShowFeedback) + { + auto* applicationModel = Env::getSingleton(); + applicationModel->showFeedback((success ? tr("Successfully saved logfile to \"%1\"") : tr("Error while saving logfile to \"%1\"")).arg(pFilename.toLocalFile())); + } + + return success; } -void LogModel::saveDummyLogFile(const QDateTime& pTimestamp) +QString LogModel::createLogFileName() const { -#ifdef QT_NO_DEBUG - Q_UNUSED(pTimestamp) -#else - auto& generator = Randomizer::getInstance().getGenerator(); - std::uniform_int_distribution dist; - const auto* logHandler = Env::getSingleton(); - const auto& copyFilename = QDir::temp().filePath(QStringLiteral("%1.%2.log").arg(QCoreApplication::applicationName()).arg(dist(generator))); - if (logHandler->copy(copyFilename) && pTimestamp.isValid()) - { - if (QFile file(copyFilename); file.open(QFile::Append)) - { - file.setFileTime(pTimestamp, QFile::FileModificationTime); - } - } - reset(); - Q_EMIT fireLogFileNamesChanged(); -#endif + const QDateTime dateTime = isCurrentLog() + ? Env::getSingleton()->getCurrentLogFileDate() + : LogHandler::getFileDate(QFileInfo(mLogFilePath)); + + auto dateFormat = QStringLiteral("yyyy-MM-dd_HH-mm"); + const QString logFileDate = dateTime.toString(dateFormat); + return QStringLiteral("%1-%2.log").arg(QCoreApplication::applicationName(), logFileDate); } @@ -307,11 +261,3 @@ Q_UNREACHABLE(); } - - -QString LogModel::createLogFileName(const QDateTime& pDateTime) -{ - auto dateFormat = QStringLiteral("yyyy-MM-dd_HH-mm"); - QString logFileDate = pDateTime.toString(dateFormat); - return QStringLiteral("%1-%2.log").arg(QCoreApplication::applicationName(), logFileDate); -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/LogModel.h ausweisapp2-2.4.0/src/ui/qml/LogModel.h --- ausweisapp2-2.3.1/src/ui/qml/LogModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/LogModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -4,9 +4,6 @@ #pragma once -#include "Env.h" -#include "SingletonCreator.h" - #include #include #include @@ -25,38 +22,28 @@ class LogModel : public QAbstractListModel - , public SingletonCreator { Q_OBJECT QML_ELEMENT - QML_SINGLETON friend class Env; friend class ::test_LogModel; - Q_PROPERTY(QStringList logFileNames READ getLogFileNames NOTIFY fireLogFileNamesChanged) + Q_PROPERTY(QString source READ getSource WRITE setSource NOTIFY fireSourceChanged) private: - QStringList mLogFiles; - int mSelectedLogFile; QStringList mLogEntries; - QSet mLevels; QSet mCategories; + QString mLogFilePath; - LogModel(); - ~LogModel() override = default; - - void reset(); void addLogEntry(const QString& pEntry); void setLogEntries(QTextStream& pTextStream); + [[nodiscard]] bool isCurrentLog() const; private Q_SLOTS: void onNewLogMsg(const QString& pMsg); - public Q_SLOTS: - void onTranslationChanged(); - public: enum LogModelRoles { @@ -66,31 +53,32 @@ MessageRole }; - QStringList getLogFileNames() const; + LogModel(); + ~LogModel() override = default; + [[nodiscard]] const QSet& getLevels() const; [[nodiscard]] const QSet& getCategories() const; - Q_INVOKABLE QDateTime getCurrentLogFileDate() const; - Q_INVOKABLE void removeOtherLogFiles(); - Q_INVOKABLE void setLogFile(int pIndex); - Q_INVOKABLE void saveCurrentLogFile(const QUrl& pFilename) const; - Q_INVOKABLE void saveDummyLogFile(const QDateTime& pTimeStamp = QDateTime()); - Q_INVOKABLE void mailLog(const QString& pEmail = QStringLiteral("support@ausweisapp.de"), - const QString& pSubject = tr("Mobile logfile"), - const QString& pMsg = tr("")) const; - + void setSource(const QString& pLogFilePath); + [[nodiscard]] QString getSource() const; + [[nodiscard]] Q_INVOKABLE bool saveLogFile(const QUrl& pFilename, bool pShowFeedback) const; + Q_INVOKABLE void mailLogFile( + const QString& pEmail = QStringLiteral("support@ausweisapp.de"), + const QString& pSubject = tr("Mobile logfile"), + const QString& pMsg = tr("")) const; // \a popupPosition will be used on an iPad as the origin of the share bubble - Q_INVOKABLE void shareLog(QPoint popupPosition) const; + Q_INVOKABLE void shareLogFile(const QPoint popupPosition) const; + [[nodiscard]] Q_INVOKABLE QString createLogFileName() const; - int rowCount(const QModelIndex& pIndex = QModelIndex()) const override; + int rowCount(const QModelIndex& pIndex) const override; QHash roleNames() const override; QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; - Q_INVOKABLE static QString createLogFileName(const QDateTime& pDateTime = QDateTime::currentDateTime()); Q_SIGNALS: - void fireLogFileNamesChanged(); void fireLevelsChanged(); void fireCategoriesChanged(); void fireNewLogMsg(); + void fireSourceChanged(); }; + } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ui/qml/LogModel_android.cpp ausweisapp2-2.4.0/src/ui/qml/LogModel_android.cpp --- ausweisapp2-2.3.1/src/ui/qml/LogModel_android.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/LogModel_android.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,12 +4,12 @@ #include "LogModel.h" -#include "LogHandler.h" - +#include #include #include #include #include +#include Q_DECLARE_LOGGING_CATEGORY(qml) @@ -18,33 +18,40 @@ using namespace governikus; -static QString getPublicLogFileName(const QDateTime& pDateTime = QDateTime::currentDateTime()) +static QString getPublicLogFilePath(const QString& pFileName) { const QStringList cachePaths = QStandardPaths::standardLocations(QStandardPaths::CacheLocation); if (cachePaths.isEmpty()) { - qCCritical(qml) << "No cache paths found!"; + qCCritical(qml) << "No cache paths found"; return QString(); } const QString cacheBasePath = cachePaths.first(); if (cacheBasePath.isEmpty()) { - qCCritical(qml) << "Cache base folder is invalid (empty)."; + qCCritical(qml) << "Cache base folder is invalid (empty)"; return QString(); } - return QStringLiteral("%1/%2").arg(cacheBasePath, LogModel::createLogFileName(pDateTime)); + return QStringLiteral("%1/%2").arg(cacheBasePath, pFileName); } -void LogModel::mailLog(const QString& pEmail, const QString& pSubject, const QString& pMsg) const +void LogModel::mailLogFile(const QString& pEmail, const QString& pSubject, const QString& pMsg) const { QJniEnvironment env; const QJniObject javaActivity(QNativeInterface::QAndroidApplication::context()); if (!javaActivity.isValid()) { - qCCritical(qml) << "Cannot determine android activity"; + qCCritical(qml) << "Can't determine android activity"; + return; + } + + const auto& publicLogFile = getPublicLogFilePath(createLogFileName()); + if (!saveLogFile(QUrl::fromLocalFile(publicLogFile), false)) + { + qCCritical(qml) << "Can't copy log file to" << publicLogFile; return; } @@ -53,17 +60,10 @@ const auto& jMsg = QJniObject::fromString(pMsg); //: LABEL ANDROID const auto& jChooserTitle = QJniObject::fromString(tr("Send application log per email...")); - const auto& publicLogFile = getPublicLogFileName(); const auto& jPublicLogFile = QJniObject::fromString(publicLogFile); - qCDebug(qml) << "Copy logfile to" << publicLogFile; - if (!Env::getSingleton()->copy(publicLogFile)) - { - qCCritical(qml) << "Cannot copy logfile to" << publicLogFile; - return; - } - - QJniObject::callStaticMethod("com/governikus/ausweisapp2/ShareUtil", + QJniObject::callStaticMethod( + "com/governikus/ausweisapp2/ShareUtil", "mailLog", "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", javaActivity.object(), @@ -81,49 +81,29 @@ } -void LogModel::shareLog(const QPoint /*popupPosition*/) const +void LogModel::shareLogFile(const QPoint /*popupPosition*/) const { QJniEnvironment env; const QJniObject javaActivity(QNativeInterface::QAndroidApplication::context()); if (!javaActivity.isValid()) { - qCCritical(qml) << "Cannot determine android activity"; + qCCritical(qml) << "Can't determine android activity"; return; } - const auto* logHandler = Env::getSingleton(); - - QString publicLogFile; - if (mSelectedLogFile == 0) + const auto& publicLogFile = getPublicLogFilePath(createLogFileName()); + if (!saveLogFile(QUrl::fromLocalFile(publicLogFile), false)) { - publicLogFile = getPublicLogFileName(); - if (!logHandler->copy(publicLogFile)) - { - qCCritical(qml) << "Cannot copy logfile to" << publicLogFile; - return; - } - } - else - { - const auto& source = mLogFiles.at(mSelectedLogFile); - const auto& dateTime = logHandler->getFileDate(QFileInfo(source)); - publicLogFile = getPublicLogFileName(dateTime); - if (QFile::exists(publicLogFile)) - { - QFile::remove(publicLogFile); - } - if (!QFile::copy(source, publicLogFile)) - { - qCCritical(qml) << "Cannot copy logfile to" << publicLogFile; - return; - } + qCCritical(qml) << "Can't copy log file to" << publicLogFile; + return; } //: LABEL ANDROID const auto& jChooserTitle = QJniObject::fromString(tr("Share application log...")); const auto& jPublicLogFile = QJniObject::fromString(publicLogFile); - QJniObject::callStaticMethod("com/governikus/ausweisapp2/ShareUtil", + QJniObject::callStaticMethod( + "com/governikus/ausweisapp2/ShareUtil", "shareLog", "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)V", javaActivity.object(), diff -Nru ausweisapp2-2.3.1/src/ui/qml/LogModel_generic.cpp ausweisapp2-2.4.0/src/ui/qml/LogModel_generic.cpp --- ausweisapp2-2.3.1/src/ui/qml/LogModel_generic.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/LogModel_generic.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -13,7 +13,7 @@ using namespace governikus; -void LogModel::mailLog(const QString& pEmail, const QString& pSubject, const QString& pMsg) const +void LogModel::mailLogFile(const QString& pEmail, const QString& pSubject, const QString& pMsg) const { Q_UNUSED(pEmail) Q_UNUSED(pSubject) @@ -23,7 +23,7 @@ } -void LogModel::shareLog(const QPoint /*popupPosition*/) const +void LogModel::shareLogFile(const QPoint /*popupPosition*/) const { qCWarning(qml) << "NOT IMPLEMENTED"; } diff -Nru ausweisapp2-2.3.1/src/ui/qml/LogModel_ios.mm ausweisapp2-2.4.0/src/ui/qml/LogModel_ios.mm --- ausweisapp2-2.3.1/src/ui/qml/LogModel_ios.mm 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/LogModel_ios.mm 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,6 @@ #include "LogModel.h" #include "ApplicationModel.h" -#include "LogHandler.h" #include "PlatformTools.h" #import @@ -13,12 +12,15 @@ #include #include +#include + Q_DECLARE_LOGGING_CATEGORY(qml) using namespace governikus; + @interface MailComposeController : MFMailComposeViewController - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error; @@ -38,45 +40,13 @@ @end -static QString getTemporaryLogFile(const QString& pSourceFile = QString()) +static QString getTemporaryLogFilePath(const QString& pTargetFileName) { - LogHandler* logHandler = Env::getSingleton(); - - QString destinationFileName; - if (pSourceFile.isEmpty()) - { - destinationFileName = LogModel::createLogFileName(); - } - else - { - destinationFileName = LogModel::createLogFileName(logHandler->getFileDate(QFileInfo(pSourceFile))); - } - - QString destinationFilePath = QString::fromNSString([NSTemporaryDirectory() stringByAppendingPathComponent: destinationFileName.toNSString()]); - - if (QFile::exists(destinationFilePath)) - { - QFile::remove(destinationFilePath); - } - - if (pSourceFile.isEmpty()) - { - logHandler->copy(destinationFilePath); - } - else - { - if (!QFile::copy(pSourceFile, destinationFilePath)) - { - qCCritical(qml) << "Cannot copy logfile to" << destinationFilePath; - return QString(); - } - } - - return destinationFilePath; + return QString::fromNSString([NSTemporaryDirectory() stringByAppendingPathComponent: pTargetFileName.toNSString()]); } -void LogModel::mailLog(const QString& pEmail, const QString& pSubject, const QString& pMsg) const +void LogModel::mailLogFile(const QString& pEmail, const QString& pSubject, const QString& pMsg) const { if (![MFMailComposeViewController canSendMail]) { @@ -84,10 +54,11 @@ return; } - QString fileName = LogModel::createLogFileName(); - const auto& logFile = getTemporaryLogFile(); - if (logFile.isEmpty()) + const auto fileName = createLogFileName(); + const auto& logFile = getTemporaryLogFilePath(fileName); + if (!saveLogFile(QUrl::fromLocalFile(logFile), false)) { + qCCritical(qml) << "Can't copy log file to" << logFile; return; } @@ -113,11 +84,12 @@ } -void LogModel::shareLog(const QPoint popupPosition) const +void LogModel::shareLogFile(const QPoint popupPosition) const { - const QString& logFile = mSelectedLogFile == 0 ? getTemporaryLogFile() : getTemporaryLogFile(mLogFiles.at(mSelectedLogFile)); - if (logFile.isEmpty()) + const auto& logFile = getTemporaryLogFilePath(createLogFileName()); + if (!saveLogFile(QUrl::fromLocalFile(logFile), false)) { + qCCritical(qml) << "Can't copy log file to" << logFile; return; } diff -Nru ausweisapp2-2.3.1/src/ui/qml/NotificationModel.cpp ausweisapp2-2.4.0/src/ui/qml/NotificationModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/NotificationModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/NotificationModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -18,17 +18,6 @@ } -QString NotificationModel::getLastType() const -{ - if (mNotificationEntries.isEmpty()) - { - return QString(); - } - - return std::as_const(mNotificationEntries).last().mType; -} - - void NotificationModel::onNewLogMsg(const QString& pMsg, const QString& pCategoryName) { if (pCategoryName == QLatin1String("developermode") || pCategoryName == QLatin1String("feedback")) @@ -44,10 +33,8 @@ beginInsertRows(QModelIndex(), size, size); //: LABEL ALL_PLATFORMS Time format according to https://doc.qt.io/qt/qtime.html#toString const auto& time = QTime::currentTime().toString(tr("hh:mm:ss")); - mNotificationEntries.append({pCategoryName, time, pMsg}); + mNotificationEntries.append({time, pMsg}); endInsertRows(); - - Q_EMIT fireLastTypeChanged(); } } @@ -66,24 +53,13 @@ return QVariant(); } const auto& notification = std::as_const(mNotificationEntries).at(mNotificationEntries.firstIndex() + pIndex.row()); - switch (pRole) - { - case TYPE: - return notification.mType; - - case TIME: - return notification.mTime; - - default: - return notification.mText; - } + return pRole == TIME ? notification.mTime : 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 -Nru ausweisapp2-2.3.1/src/ui/qml/NotificationModel.h ausweisapp2-2.4.0/src/ui/qml/NotificationModel.h --- ausweisapp2-2.3.1/src/ui/qml/NotificationModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/NotificationModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -31,19 +31,15 @@ friend class Env; friend class ::test_NotificationModel; - Q_PROPERTY(QString lastType READ getLastType NOTIFY fireLastTypeChanged) - private: enum UserRoles { - TYPE = Qt::UserRole + 1, - TIME, + TIME = Qt::UserRole + 1, TEXT }; struct NotificationEntry { - QString mType; QString mTime; QString mText; }; @@ -52,7 +48,6 @@ NotificationModel(); ~NotificationModel() override = default; - QString getLastType() const; private Q_SLOTS: void onNewLogMsg(const QString& pMsg, const QString& pCategoryName); @@ -61,9 +56,6 @@ [[nodiscard]] int rowCount(const QModelIndex& pIndex) const override; [[nodiscard]] QVariant data(const QModelIndex& pIndex, int pRole) const override; [[nodiscard]] QHash roleNames() const override; - - Q_SIGNALS: - void fireLastTypeChanged(); }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ui/qml/NumberModel.cpp ausweisapp2-2.4.0/src/ui/qml/NumberModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/NumberModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/NumberModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -12,6 +12,16 @@ #include "context/PersonalizationContext.h" #endif +namespace +{ +QString addBoldFormatting(const QString& inputString) +{ + return inputString.arg(QStringLiteral(""), QStringLiteral("")); +} + + +} // namespace + using namespace governikus; NumberModel::NumberModel() @@ -309,19 +319,19 @@ if (getPasswordType() == PasswordType::CAN && !isCanAllowedMode()) { - return QStringLiteral("%1

%2").arg( - //: INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. Part 1/2 - tr("An incorrect PIN has been entered 2 times at the last use of your ID card."), - //: INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. Part 2/2 - tr("For a 3rd attempt, the 6-digit Card Access Number (CAN) must be entered first. You can find your CAN in the bottom right on the front of your ID card.")); + return addBoldFormatting(QStringLiteral("%1

%2").arg( + //: INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. %1 + %2 are used to emphasize. Part 1/2 + tr("%1An incorrect PIN has been entered 2 times%2 at the last use of your ID card."), + //: INFO ALL_PLATFORMS Once per workflow info text shown when an ID card with one PIN attempt left has been detected. %1 + %2 are used to emphasize. Part 2/2 + tr("For a 3rd attempt, the%1 6-digit Card Access Number (CAN)%2 must be entered first. You can find your CAN %1in the bottom right on the front of your ID card%2."))); } if (getPasswordType() == PasswordType::PUK) { - return QStringLiteral("%1

%2").arg( - //: INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. Part 1/2 - tr("An incorrect PIN has been entered 3 times at the last use of your ID card."), - //: INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. Part 2/2 - tr("Therefor you have to enter the PUK first to unlock the ID card PIN.")); + return addBoldFormatting(QStringLiteral("%1

%2").arg( + //: INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. %1 + %2 are used to emphasize. Part 1/2 + tr("%1An incorrect PIN has been entered 3 times%2 at the last use of your ID card."), + //: INFO ALL_PLATFORMS Once per workflow info text shown when a blocked ID card has been detected. %1 + %2 are used to emphasize. Part 2/2 + tr("Therefore you have to enter the %1PUK%2 first to %1unlock the ID card PIN%2."))); } } return QString(); @@ -368,57 +378,57 @@ return QStringLiteral("%1

%2").arg( //: INFO ALL_PLATFORMS The wrong Transport PIN was entered on the first attempt. Part 1/2 tr("You have entered an incorrect, 5-digit Transport PIN."), - //: INFO ALL_PLATFORMS The wrong Transport PIN was entered on the first attempt.Part 2/2 - tr("You have 2 further attempts to enter the correct Transport PIN. " - "The 5-digit Transport PIN may be found on the bottom left of your PIN letter.")); + //: INFO ALL_PLATFORMS The wrong Transport PIN was entered on the first attempt. %1 + %2 are used to emphasize. Part 2/2 + addBoldFormatting(tr("You have%1 2 further attempts%2 to enter the correct Transport PIN. " + "The 5-digit Transport PIN may be found on the %1bottom left of your PIN letter%2."))); } else if (isSmartCard) { - //: INFO ALL_PLATFORMS The wrong Smart-eID PIN was entered on the first attempt. - return tr("You have entered an incorrect, 6-digit Smart-eID PIN. You have 2 further attempts to enter the correct Smart-eID PIN."); + //: INFO ALL_PLATFORMS The wrong Smart-eID PIN was entered on the first attempt. %1 + %2 are used to emphasize. + return(tr("You have entered an incorrect, 6-digit Smart-eID PIN. You have%1 2 further attempts%2 to enter the correct Smart-eID PIN.")); } else { return QStringLiteral("%1

%2").arg( //: INFO ALL_PLATFORMS The wrong ID card PIN was entered on the first attempt. Part 1/2 tr("You have entered an incorrect, 6-digit ID card PIN."), - //: INFO ALL_PLATFORMS The wrong ID card PIN was entered on the first attempt. Part 2/2 - tr("You have 2 further attempts to enter the correct ID card PIN.")); + //: INFO ALL_PLATFORMS The wrong ID card PIN was entered on the first attempt. %1 + %2 are used to emphasize. Part 2/2 + addBoldFormatting(tr("You have%1 2 further attempts%2 to enter the correct ID card PIN."))); } case CardReturnCode::INVALID_PIN_2: if (isRequestTransportPin) { - return QStringLiteral("%1

%2").arg( - //: INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. Part 1/2 - tr("You have entered an incorrect, 5-digit Transport PIN 2 times."), - //: INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. Part 2/2 - tr("For a 3rd attempt, the 6-digit Card Access Number (CAN) must be entered first. " - "You can find your CAN in the bottom right on the front of your ID card.")); + return addBoldFormatting(QStringLiteral("%1

%2").arg( + //: INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 1/2 + tr("You have entered an %1incorrect, 5-digit Transport PIN 2 times%2."), + //: INFO ALL_PLATFORMS The wrong Transport PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 2/2 + tr("For a 3rd attempt, the%1 6-digit Card Access Number (CAN)%2 must be entered first. " + "You can find your CAN in the %1bottom right on the front of your ID card%2."))); } else if (isSmartCard) { - //: INFO ANDROID IOS The wrong Smart-eID PIN was entered twice, a 3rd wrong attempt could invalidate the Smart-eID. - return tr("You have entered an incorrect, 6-digit Smart-eID PIN 2 times. " - "After the next failed attempt you will no longer be able to use your Smart-eID and will need to set it up again."); + //: INFO ANDROID IOS The wrong Smart-eID PIN was entered twice, a 3rd wrong attempt could invalidate the Smart-eID. %1 + %2 are used to emphasize. + return addBoldFormatting(tr("You have entered an %1incorrect, 6-digit Smart-eID PIN 2 times%2. " + "After the next failed attempt you will no longer be able to use your Smart-eID and will need to set it up again.")); } else { - return QStringLiteral("%1

%2").arg( - //: INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. Part 1/2 - tr("You have entered an incorrect, 6-digit ID card PIN 2 times."), - //: INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. Part 2/2 - tr("For a 3rd attempt, the 6-digit Card Access Number (CAN) must be entered first. " - "You can find your CAN in the bottom right on the front of your ID card.")); + return addBoldFormatting(QStringLiteral("%1

%2").arg( + //: INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 1/2 + tr("You have entered an %1incorrect, 6-digit ID card PIN 2 times%2."), + //: INFO ALL_PLATFORMS The wrong ID card PIN was entered twice, the next attempt requires the CAN for additional verification. %1 + %2 are used to emphasize. Part 2/2 + tr("For a 3rd attempt, the%1 6-digit Card Access Number (CAN)%2 must be entered first. " + "You can find your CAN in the %1bottom right on the front of your ID card%2."))); } case CardReturnCode::INVALID_PIN_3: if (isRequestTransportPin) { - //: INFO ALL_PLATFORMS The Transport PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. - return tr("You have entered an incorrect, 5-digit Transport PIN 3 times, your Transport PIN is now blocked. " - "To remove the block, the 10-digit PUK must be entered first."); + //: INFO ALL_PLATFORMS The Transport PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. %1 + %2 are used to emphasize. + return addBoldFormatting(tr("You have entered an incorrect, 5-digit Transport PIN 3 times, your %1Transport PIN is now blocked%2. " + "To remove the block, the%1 10-digit PUK%2 must be entered first.")); } else if (isSmartCard) { @@ -429,18 +439,18 @@ } else { - return QStringLiteral("%1

%2").arg( + return addBoldFormatting(QStringLiteral("%1

%2").arg( //: INFO ALL_PLATFORMS The ID card PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. Part 1/2 - tr("You have entered an incorrect, 6-digit ID card PIN 3 times. Your ID card PIN is now blocked."), + tr("You have entered an incorrect, 6-digit ID card PIN 3 times. Your %1ID card PIN is now blocked%2."), //: INFO ALL_PLATFORMS The ID card PIN was entered wrongfully three times, the ID card needs to be unlocked using the PUK. Part 2/2 - tr("To remove the block, the 10-digit PUK must be entered first. " - "You can find the PUK in the bottom right next to the Transport PIN in the authority's letter.")); + tr("To remove the block, the%1 10-digit PUK%2 must be entered first. " + "You can find the PUK in the bottom %1right next%2 to the Transport PIN in the %1authority's letter%2."))); } case CardReturnCode::INVALID_CAN: - //: INFO ALL_PLATFORMS The CAN was entered wrongfully and needs to be supplied again. - return tr("You have entered an incorrect Card Access Number (CAN). Please try again. " - "You can find your CAN in the bottom right on the front of your ID card."); + //: INFO ALL_PLATFORMS The CAN was entered wrongfully and needs to be supplied again. %1 + %2 are used to emphasize. + return addBoldFormatting(tr("You have entered an %1incorrect Card Access Number (CAN)%2. Please try again. " + "You can find your CAN in the %1bottom right on the front of your ID card%2.")); case CardReturnCode::INVALID_PUK: //: INFO ALL_PLATFORMS The PUK entered wrongfully and needs to be supplied again. diff -Nru ausweisapp2-2.3.1/src/ui/qml/PinResetInformationModel.cpp ausweisapp2-2.4.0/src/ui/qml/PinResetInformationModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/PinResetInformationModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/PinResetInformationModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -27,6 +27,22 @@ } +QUrl PinResetInformationModel::getAdministrativeSearchUrl() const +{ + if (LanguageLoader::getLocaleCode() == QLatin1String("de")) + { + return QUrl(QStringLiteral("https://servicesuche.bund.de")); + } + return QUrl(QStringLiteral("https://servicesuche.bund.de/#/en")); +} + + +QUrl PinResetInformationModel::getPinResetActivationUrl() const +{ + return QUrl(QStringLiteral("https://www.pin-ruecksetzbrief-bestellen.de/aktivierung")); +} + + QUrl PinResetInformationModel::getPinResetUrl() const { const auto* config = Env::getSingleton(); @@ -35,19 +51,15 @@ if (homepage.isEmpty()) { - if (LanguageLoader::getLocaleCode() == QLatin1String("de")) - { - return QStringLiteral("https://servicesuche.bund.de"); - } - return QStringLiteral("https://servicesuche.bund.de/#/en"); + return getAdministrativeSearchUrl(); } if (LanguageLoader::getLocaleCode() != QLatin1String("de")) { - return homepage + QStringLiteral("/en"); + return QUrl(homepage + QStringLiteral("/en")); } - return homepage; + return QUrl(homepage); } @@ -145,7 +157,7 @@ serviceSearchPage += QStringLiteral("/#/en"); } //: LABEL ALL_PLATFORMS %1 will be replaced with a link to a website. - return QStringLiteral("

") + tr("To find your competent authority you may visit %1 .").arg(QStringLiteral("servicesuche.bund.de").arg(serviceSearchPage)); + return QStringLiteral("

") + tr("To find your competent authority you may visit %1 .").arg(QStringLiteral("servicesuche.bund.de").arg(serviceSearchPage)); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/PinResetInformationModel.h ausweisapp2-2.4.0/src/ui/qml/PinResetInformationModel.h --- ausweisapp2-2.3.1/src/ui/qml/PinResetInformationModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/PinResetInformationModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -23,7 +23,10 @@ friend class Env; + Q_PROPERTY(bool hasPinResetService READ hasPinResetService NOTIFY fireUpdated) Q_PROPERTY(QUrl pinResetUrl READ getPinResetUrl NOTIFY fireUpdated) + Q_PROPERTY(QUrl pinResetActivationUrl READ getPinResetActivationUrl CONSTANT) + Q_PROPERTY(QUrl administrativeSearchUrl READ getAdministrativeSearchUrl NOTIFY fireUpdated) Q_PROPERTY(QString noPinAndNoPukHint READ getNoPinAndNoPukHint NOTIFY fireUpdated) Q_PROPERTY(QString requestNewPinHint READ getRequestNewPinHint NOTIFY fireUpdated) Q_PROPERTY(QString activateOnlineFunctionHint READ getActivateOnlineFunctionHint NOTIFY fireUpdated) @@ -36,10 +39,13 @@ private: PinResetInformationModel(); ~PinResetInformationModel() override = default; - bool hasPinResetService() const; public: + bool hasPinResetService() const; + [[nodiscard]] QUrl getPinResetUrl() const; + [[nodiscard]] QUrl getAdministrativeSearchUrl() const; + [[nodiscard]] QUrl getPinResetActivationUrl() const; [[nodiscard]] QString getNoPinAndNoPukHint() const; [[nodiscard]] QString getRequestNewPinHint() const; [[nodiscard]] QString getActivateOnlineFunctionHint() const; diff -Nru ausweisapp2-2.3.1/src/ui/qml/ReaderModel.cpp ausweisapp2-2.4.0/src/ui/qml/ReaderModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/ReaderModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/ReaderModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -77,7 +77,7 @@ return false; } - if (pIndex.row() >= rowCount()) + if (pIndex.row() >= rowCount(pIndex)) { Q_ASSERT(false && "Invoked with a row which is out of bounds."); return false; diff -Nru ausweisapp2-2.3.1/src/ui/qml/ReaderModel.h ausweisapp2-2.4.0/src/ui/qml/ReaderModel.h --- ausweisapp2-2.3.1/src/ui/qml/ReaderModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/ReaderModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -69,7 +69,7 @@ READER_SUPPORTED }; - [[nodiscard]] int rowCount(const QModelIndex& pParent = QModelIndex()) const override; + [[nodiscard]] int rowCount(const QModelIndex& pParent) const override; [[nodiscard]] QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; [[nodiscard]] QHash roleNames() const override; diff -Nru ausweisapp2-2.3.1/src/ui/qml/RemoteDeviceModel.cpp ausweisapp2-2.4.0/src/ui/qml/RemoteDeviceModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/RemoteDeviceModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/RemoteDeviceModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -84,8 +84,8 @@ #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) if (pRemoteDeviceModelEntry.isPairing()) { - //: LABEL ALL_PLATFORMS - return tr("Click to pair"); + //: LABEL LABEL ANDROID IOS + return tr("Tap to pair"); } #endif @@ -127,12 +127,12 @@ } const QSharedPointer deviceListEntry = availableReader.getRemoteDeviceListEntry(); - if (deviceListEntry.isNull() || deviceListEntry->getIfdDescriptor().isNull()) + if (deviceListEntry.isNull() || deviceListEntry->getDiscovery().addressesMissing()) { break; } - return deviceListEntry->getIfdDescriptor().getIfdName(); + return deviceListEntry->getDiscovery().getIfdName(); } return {}; } @@ -254,7 +254,7 @@ if (!mAllRemoteReaders.contains(pModelEntry)) { const auto readerCount = static_cast(mAllRemoteReaders.size()); - beginInsertRows(index(readerCount, 0), readerCount, readerCount); + beginInsertRows(QAbstractListModel::index(readerCount, 0), readerCount, readerCount); mAllRemoteReaders.append(pModelEntry); endInsertRows(); return true; @@ -262,7 +262,7 @@ const auto readerIndex = mAllRemoteReaders.indexOf(pModelEntry); mAllRemoteReaders[readerIndex] = pModelEntry; - const auto modelIndex = index(static_cast(readerIndex), 0); + const auto modelIndex = QAbstractListModel::index(static_cast(readerIndex), 0); Q_EMIT dataChanged(modelIndex, modelIndex); return false; } @@ -420,7 +420,7 @@ mPairedReaders.clear(); const RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); - auto pairedReaders = settings.getRemoteInfos(); + const auto pairedReaders = settings.getRemoteInfos(); for (const auto& reader : pairedReaders) { mPairedReaders[reader.getFingerprint()] = reader; diff -Nru ausweisapp2-2.3.1/src/ui/qml/RemoteDeviceModel.h ausweisapp2-2.4.0/src/ui/qml/RemoteDeviceModel.h --- ausweisapp2-2.3.1/src/ui/qml/RemoteDeviceModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/RemoteDeviceModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -52,7 +52,7 @@ void updatePairedReaders(); void updateUnpairedReaders(); void removeVanishedReaders(); - [[nodiscard]] virtual QList presentReaders() const; + [[nodiscard]] QList presentReaders() const; bool addOrUpdateReader(const RemoteDeviceModelEntry& pModelEntry); private Q_SLOTS: diff -Nru ausweisapp2-2.3.1/src/ui/qml/RemoteDeviceModelEntry.cpp ausweisapp2-2.4.0/src/ui/qml/RemoteDeviceModelEntry.cpp --- ausweisapp2-2.3.1/src/ui/qml/RemoteDeviceModelEntry.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/RemoteDeviceModelEntry.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -11,13 +11,13 @@ RemoteDeviceModelEntry::RemoteDeviceModelEntry(const QSharedPointer& pListEntry) - : mDeviceName(RemoteServiceSettings::escapeDeviceName(pListEntry->getIfdDescriptor().getIfdName())) - , mId(pListEntry->getIfdDescriptor().getIfdId()) + : mDeviceName(RemoteServiceSettings::escapeDeviceName(pListEntry->getDiscovery().getIfdName())) + , mId(pListEntry->getDiscovery().getIfdId()) , mPaired(false) - , mIsPairing(pListEntry->getIfdDescriptor().isPairingAnnounced()) + , mIsPairing(pListEntry->getDiscovery().isPairing()) , mNetworkVisible(false) , mConnected(false) - , mSupported(pListEntry->getIfdDescriptor().isSupported()) + , mSupported(pListEntry->getDiscovery().isSupported()) , mLastConnected() , mRemoteDeviceListEntry(pListEntry) { diff -Nru ausweisapp2-2.3.1/src/ui/qml/RemoteServiceModel.cpp ausweisapp2-2.4.0/src/ui/qml/RemoteServiceModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/RemoteServiceModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/RemoteServiceModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -294,7 +294,7 @@ const auto* const ifdClient = Env::getSingleton(); disconnect(ifdClient, &IfdClient::fireEstablishConnectionDone, this, &RemoteServiceModel::onEstablishConnectionDone); qDebug() << "Pairing finished:" << pStatus; - const auto deviceName = RemoteServiceSettings::escapeDeviceName(pEntry->getIfdDescriptor().getIfdName()); + const auto deviceName = RemoteServiceSettings::escapeDeviceName(pEntry->getDiscovery().getIfdName()); if (pStatus.isError()) { Q_EMIT firePairingFailed(deviceName, pStatus.toErrorDescription()); diff -Nru ausweisapp2-2.3.1/src/ui/qml/SelfAuthModel.cpp ausweisapp2-2.4.0/src/ui/qml/SelfAuthModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/SelfAuthModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/SelfAuthModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -33,7 +33,7 @@ { Q_ASSERT(!mSelfData.isEmpty()); const auto& [lastKey, lastValue] = mSelfData.takeLast(); - mSelfData << qMakePair(lastKey, lastValue + QLatin1Char('\n') + value); + mSelfData << std::make_pair(lastKey, lastValue + QLatin1Char('\n') + value); } else { @@ -102,7 +102,7 @@ QVariant SelfAuthModel::data(const QModelIndex& pIndex, int pRole) const { - if (pIndex.isValid() && pIndex.row() < rowCount()) + if (pIndex.isValid() && pIndex.row() < rowCount(pIndex)) { const auto& [name, value] = mSelfData.at(pIndex.row()); if (pRole == Qt::DisplayRole || pRole == NAME) diff -Nru ausweisapp2-2.3.1/src/ui/qml/SelfAuthModel.h ausweisapp2-2.4.0/src/ui/qml/SelfAuthModel.h --- ausweisapp2-2.3.1/src/ui/qml/SelfAuthModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/SelfAuthModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -60,7 +60,7 @@ [[nodiscard]] bool isWorkflowCancelled() const; [[nodiscard]] Q_INVOKABLE bool isBasicReader() const; - [[nodiscard]] int rowCount(const QModelIndex& = QModelIndex()) const override; + [[nodiscard]] int rowCount(const QModelIndex&) const override; [[nodiscard]] QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; [[nodiscard]] QHash roleNames() const override; diff -Nru ausweisapp2-2.3.1/src/ui/qml/SettingsModel.cpp ausweisapp2-2.4.0/src/ui/qml/SettingsModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/SettingsModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/SettingsModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,7 +6,6 @@ #include "AppSettings.h" #include "LanguageLoader.h" -#include "ReaderManager.h" #include "Service.h" #include "VolatileSettings.h" @@ -25,8 +24,9 @@ , mAdvancedSettings(false) , mIsStartedByAuth(false) , mShowBetaTesting(!qEnvironmentVariableIsSet("SUPPRESS_BETA_LOGO")) + , mManualAppcastUpdateRequested(false) { - connect(Env::getSingleton(), &AppUpdateDataModel::fireAppUpdateDataChanged, this, &SettingsModel::fireAppUpdateDataChanged); + connect(Env::getSingleton(), &AppUpdateDataModel::fireAppUpdateDataChanged, this, &SettingsModel::onAppUpdateDataChanged); const auto& generalSettings = Env::getSingleton()->getGeneralSettings(); connect(&generalSettings, &GeneralSettings::fireShowInAppNotificationsChanged, this, &SettingsModel::fireShowInAppNotificationsChanged); @@ -37,12 +37,15 @@ connect(&generalSettings, &GeneralSettings::fireDarkModeChanged, this, &SettingsModel::fireDarkModeChanged); connect(&generalSettings, &GeneralSettings::firePreferredTechnologyChanged, this, &SettingsModel::firePreferredTechnologyChanged); + connect(&generalSettings, &GeneralSettings::fireSettingsChanged, this, &SettingsModel::fireRemindUserOfAutoRedirectChanged); + const auto& simulatorSettings = Env::getSingleton()->getSimulatorSettings(); connect(&simulatorSettings, &SimulatorSettings::fireEnabledChanged, this, &SettingsModel::fireSimulatorChanged); connect(&simulatorSettings, &SimulatorSettings::fireEnabledChanged, this, &SettingsModel::firePreferredTechnologyChanged); - const auto* readerManager = Env::getSingleton(); - connect(readerManager, &ReaderManager::fireStatusChanged, this, &SettingsModel::firePreferredTechnologyChanged); + connect(Env::getSingleton(), &ApplicationModel::fireNfcStateChanged, this, [this]{ + onNfcStateChanged(Env::getSingleton()->getNfcState()); + }); #ifdef Q_OS_ANDROID mIsStartedByAuth = QJniObject::callStaticMethod("com/governikus/ausweisapp2/MainActivity", "isStartedByAuth"); @@ -475,6 +478,22 @@ } +bool SettingsModel::isRemindUserOfAutoRedirect() const +{ + return Env::getSingleton()->getGeneralSettings().isRemindUserOfAutoRedirect(); +} + + +void SettingsModel::setRemindUserOfAutoRedirect(bool pRemindUser) const +{ + if (isRemindUserOfAutoRedirect() != pRemindUser) + { + auto& settings = Env::getSingleton()->getGeneralSettings(); + settings.setRemindUserOfAutoRedirect(pRemindUser); + } +} + + bool SettingsModel::isTransportPinReminder() const { return Env::getSingleton()->getGeneralSettings().isTransportPinReminder(); @@ -509,8 +528,9 @@ } -void SettingsModel::updateAppcast() const +void SettingsModel::updateAppcast() { + mManualAppcastUpdateRequested = true; Env::getSingleton()->updateAppcast(); } @@ -543,7 +563,7 @@ } url.setHost(settings.getCustomProxyHost()); url.setPort(settings.getCustomProxyPort()); - return url.toString(); + return url; } @@ -597,7 +617,7 @@ { return Enum::fromString( Env::getSingleton()->getGeneralSettings().getDarkMode(), - ModeOption::OFF); + ModeOption::AUTO); } @@ -614,8 +634,7 @@ const auto simulator = Env::getSingleton()->getSimulatorSettings().isEnabled(); if (technology == ReaderManagerPluginType::UNKNOWN || (technology == ReaderManagerPluginType::SIMULATOR && !simulator)) { - const auto& pluginInfo = Env::getSingleton()->getPluginInfo(ReaderManagerPluginType::NFC); - if (pluginInfo.isAvailable()) + if (Env::getSingleton()->getNfcState() != ApplicationModel::NfcState::UNAVAILABLE) { return ReaderManagerPluginType::NFC; } @@ -640,6 +659,7 @@ GeneralSettings& settings = Env::getSingleton()->getGeneralSettings(); settings.setTransportPinReminder(true); settings.setRemindUserToClose(true); + settings.setRemindUserOfAutoRedirect(true); settings.setRequestStoreFeedback(true); settings.setStartupModule(QString()); settings.setShowOnboarding(true); @@ -695,3 +715,27 @@ Q_EMIT fireDeveloperOptionsChanged(); #endif } + + +void SettingsModel::onAppUpdateDataChanged() +{ + if (mManualAppcastUpdateRequested) + { + mManualAppcastUpdateRequested = false; + Q_EMIT fireAppUpdateDataChanged(true); + return; + } + Q_EMIT fireAppUpdateDataChanged(false); +} + + +void SettingsModel::onNfcStateChanged(ApplicationModel::NfcState pNfcState) +{ + if (pNfcState == ApplicationModel::NfcState::UNAVAILABLE && getPreferredTechnology() == ReaderManagerPluginType::NFC) + { + setPreferredTechnology(ReaderManagerPluginType::UNKNOWN); + return; + } + + Q_EMIT firePreferredTechnologyChanged(); +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/SettingsModel.h ausweisapp2-2.4.0/src/ui/qml/SettingsModel.h --- ausweisapp2-2.3.1/src/ui/qml/SettingsModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/SettingsModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -5,6 +5,7 @@ #pragma once #include "AppUpdateDataModel.h" +#include "ApplicationModel.h" #include "Env.h" #include "ReaderManagerPluginInfo.h" #include "SingletonCreator.h" @@ -14,9 +15,11 @@ #include #include + +class test_SettingsModel; + namespace governikus { - class SettingsModel : public QObject , public SingletonCreator @@ -27,6 +30,7 @@ QML_SINGLETON friend class Env; + friend class ::test_SettingsModel; Q_PROPERTY(QString language READ getLanguage WRITE setLanguage NOTIFY fireLanguageChanged) Q_PROPERTY(bool advancedSettings READ isAdvancedSettings WRITE setAdvancedSettings NOTIFY fireAdvancedSettingsChanged) @@ -56,6 +60,7 @@ 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 remindUserOfAutoRedirect READ isRemindUserOfAutoRedirect WRITE setRemindUserOfAutoRedirect NOTIFY fireRemindUserOfAutoRedirectChanged) Q_PROPERTY(bool transportPinReminder READ isTransportPinReminder WRITE setTransportPinReminder NOTIFY fireTransportPinReminderChanged) Q_PROPERTY(bool showInAppNotifications READ isShowInAppNotifications WRITE setShowInAppNotifications NOTIFY fireShowInAppNotificationsChanged) Q_PROPERTY(governikus::AppUpdateDataModel * appUpdateData READ getAppUpdateData NOTIFY fireAppUpdateDataChanged) @@ -71,10 +76,15 @@ bool mAdvancedSettings; bool mIsStartedByAuth; bool mShowBetaTesting; + bool mManualAppcastUpdateRequested; SettingsModel(); ~SettingsModel() override = default; + private Q_SLOTS: + void onAppUpdateDataChanged(); + void onNfcStateChanged(ApplicationModel::NfcState pNfcState); + public: enum class ModeOption { @@ -154,6 +164,9 @@ [[nodiscard]] bool isRemindUserToClose() const; void setRemindUserToClose(bool pRemindUser); + [[nodiscard]] bool isRemindUserOfAutoRedirect() const; + void setRemindUserOfAutoRedirect(bool pRemindUser) const; + [[nodiscard]] bool isTransportPinReminder() const; void setTransportPinReminder(bool pTransportPinReminder); @@ -180,7 +193,7 @@ [[nodiscard]] Q_INVOKABLE bool requestStoreFeedback() const; Q_INVOKABLE void hideFutureStoreFeedbackDialogs() const; - Q_INVOKABLE void updateAppcast() const; + Q_INVOKABLE void updateAppcast(); [[nodiscard]] AppUpdateDataModel* getAppUpdateData() const; @@ -211,8 +224,9 @@ void fireAutoRedirectAfterAuthenticationChanged(); void fireAutoUpdateCheckChanged(); void fireRemindUserToCloseChanged(); + void fireRemindUserOfAutoRedirectChanged(); void fireTransportPinReminderChanged(); - void fireAppUpdateDataChanged(); + void fireAppUpdateDataChanged(bool pAfterManualRequest); void fireShowInAppNotificationsChanged(); void fireUseCustomProxyChanged(); void fireUseSystemFontChanged(); diff -Nru ausweisapp2-2.3.1/src/ui/qml/SurveyModel.cpp ausweisapp2-2.4.0/src/ui/qml/SurveyModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/SurveyModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/SurveyModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -36,7 +36,7 @@ QVariant SurveyModel::data(const QModelIndex& pIndex, int pRole) const { - if (pIndex.isValid() && pIndex.row() < rowCount()) + if (pIndex.isValid() && pIndex.row() < rowCount(pIndex)) { const auto& [title, value] = mData[pIndex.row()]; if (pRole == TITLE) @@ -69,5 +69,5 @@ void SurveyModel::setDeviceSurveyPending(bool pValue) const { - return Env::getSingleton()->setDeviceSurveyPending(pValue); + Env::getSingleton()->setDeviceSurveyPending(pValue); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/SurveyModel.h ausweisapp2-2.4.0/src/ui/qml/SurveyModel.h --- ausweisapp2-2.3.1/src/ui/qml/SurveyModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/SurveyModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -39,7 +39,7 @@ void onSurveyDataChanged(); public: - [[nodiscard]] int rowCount(const QModelIndex& = QModelIndex()) const override; + [[nodiscard]] int rowCount(const QModelIndex&) const override; [[nodiscard]] QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; [[nodiscard]] QHash roleNames() const override; diff -Nru ausweisapp2-2.3.1/src/ui/qml/UiPluginModel.cpp ausweisapp2-2.4.0/src/ui/qml/UiPluginModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/UiPluginModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/UiPluginModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -41,3 +41,17 @@ mUpdateInformationPending = pNewIsUpdatePending; Q_EMIT fireIsUpdatePendingChanged(); } + + +bool UiPluginModel::showUpdateInformationIfPending() +{ + if (!isUpdatePending()) + { + return false; + } + + setUpdatePending(false); + onShowUi(UiModule::UPDATEINFORMATION); + + return true; +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/UiPluginModel.h ausweisapp2-2.4.0/src/ui/qml/UiPluginModel.h --- ausweisapp2-2.3.1/src/ui/qml/UiPluginModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/UiPluginModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -31,15 +31,17 @@ Q_PROPERTY(bool dominated READ isDominated NOTIFY fireDominatorChanged) Q_PROPERTY(QVariantMap safeAreaMargins READ getSafeAreaMargins NOTIFY fireSafeAreaMarginsChanged) Q_PROPERTY(bool highContrastEnabled READ isHighContrastEnabled NOTIFY fireHighContrastEnabledChanged) - Q_PROPERTY(bool osDarkModeSupported READ isOsDarkModeSupported CONSTANT) Q_PROPERTY(bool darkModeEnabled READ isDarkModeEnabled NOTIFY fireDarkModeEnabledChanged) Q_PROPERTY(QString fixedFontFamily READ getFixedFontFamily CONSTANT) Q_PROPERTY(QSize initialWindowSize READ getInitialWindowSize CONSTANT) Q_PROPERTY(bool showFocusIndicator READ getShowFocusIndicator NOTIFY fireShowFocusIndicator) Q_PROPERTY(qreal scaleFactor READ getScaleFactor WRITE setScaleFactor NOTIFY fireScaleFactorChanged) Q_PROPERTY(qreal fontScaleFactor READ getFontScaleFactor NOTIFY fireFontScaleFactorChanged) + Q_PROPERTY(int fontWeightAdjustment READ getFontWeightAdjustment NOTIFY fireFontWeightAdjustmentChanged) Q_PROPERTY(bool isChromeOS READ isChromeOS CONSTANT) Q_PROPERTY(bool isUpdatePending READ isUpdatePending NOTIFY fireIsUpdatePendingChanged) + Q_PROPERTY(bool a11yButtonShapeActive READ isA11yButtonShapeActive NOTIFY fireA11yButtonShapeActiveChanged) + Q_PROPERTY(bool a11yOnOffSwitchLabelActive READ isA11yOnOffSwitchLabelActive NOTIFY fireA11yOnOffSwitchLabelActiveChanged) bool mUpdateInformationPending; @@ -57,7 +59,6 @@ [[nodiscard]] virtual bool isDominated() const = 0; [[nodiscard]] virtual QVariantMap getSafeAreaMargins() const = 0; [[nodiscard]] virtual bool isHighContrastEnabled() const = 0; - [[nodiscard]] virtual bool isOsDarkModeSupported() const = 0; [[nodiscard]] virtual bool isDarkModeEnabled() const = 0; [[nodiscard]] virtual QString getFixedFontFamily() const = 0; [[nodiscard]] virtual QSize getInitialWindowSize() const = 0; @@ -65,10 +66,14 @@ [[nodiscard]] virtual qreal getScaleFactor() const = 0; virtual void setScaleFactor(qreal pScaleFactor) = 0; [[nodiscard]] virtual qreal getFontScaleFactor() const = 0; + [[nodiscard]] virtual int getFontWeightAdjustment() const = 0; [[nodiscard]] virtual bool isChromeOS() const = 0; + [[nodiscard]] virtual bool isA11yButtonShapeActive() const = 0; + [[nodiscard]] virtual bool isA11yOnOffSwitchLabelActive() const = 0; Q_INVOKABLE virtual void hideFromTaskbar() const = 0; Q_INVOKABLE virtual void doRefresh() = 0; + Q_INVOKABLE bool showUpdateInformationIfPending(); [[nodiscard]] bool isUpdatePending() const; @@ -86,7 +91,10 @@ void fireShowFocusIndicator(); void fireScaleFactorChanged(); void fireFontScaleFactorChanged(); + void fireFontWeightAdjustmentChanged(); void fireIsUpdatePendingChanged(); + void fireA11yButtonShapeActiveChanged(); + void fireA11yOnOffSwitchLabelActiveChanged(); }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ui/qml/UiPluginQml.cpp ausweisapp2-2.4.0/src/ui/qml/UiPluginQml.cpp --- ausweisapp2-2.3.1/src/ui/qml/UiPluginQml.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/UiPluginQml.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -28,12 +28,6 @@ #include "context/ChangePinContext.h" #include "context/SelfAuthContext.h" -#ifdef USE_CUSTOM_REGISTRATION - #include "DiagnosisModel.h" - #include "SectionModel.h" -#endif - - #if __has_include("context/PersonalizationContext.h") #include "context/PersonalizationContext.h" #endif @@ -63,12 +57,8 @@ #include #include #include -#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) - #include -#endif -#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) - #include -#endif +#include +#include #include #include @@ -88,26 +78,6 @@ qRegisterMetaType>("QList"); }) -#ifdef Q_OS_WIN -namespace -{ -void updateTitleBarColor(QQuickWindow* pWindow, bool pDarkMode) -{ - if (pWindow) - { - BOOL darkMode = pDarkMode ? TRUE : FALSE; - HWND hwnd = reinterpret_cast(pWindow->winId()); - #if defined(__MINGW32__) || !defined(NTDDI_WIN10_CO) - const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; - #endif - DwmSetWindowAttribute(hwnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &darkMode, sizeof(darkMode)); - } -} - - -} // namespace -#endif - UiPluginQml::UiPluginQml() : UiPluginModel() @@ -120,15 +90,16 @@ , mHighContrastEnabled(false) , mDarkMode(false) , mShowFocusIndicator(false) - , mScaleFactor(DEFAULT_SCALE_FACTOR) + , mScaleFactor(1.0) , mFontScaleFactor(getSystemFontScaleFactor()) + , mFontWeightAdjustment(getSystemFontWeightAdjustment()) + , mA11yButtonShapeActive(getA11yButtonShapeActive()) + , mA11yOnOffSwitchLabelActive(getA11yOnOffSwitchLabelActive()) #ifdef Q_OS_IOS , mPrivate(new Private()) #endif { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) QSvgRenderer::setDefaultOptions(QtSvg::AssumeTrustedSource); -#endif Env::getSingleton()->setUsedAsSDK(false); @@ -149,26 +120,26 @@ connect(this, &UiPluginQml::fireAppConfigChanged, this, &UiPluginQml::onAppConfigChanged); onAppConfigChanged(); + connect(Env::getSingleton(), &ReaderManager::fireStatusChanged, this, &UiPluginQml::onReaderStatusChanged); + qApp->installEventFilter(this); } -void UiPluginQml::registerQmlTypes() +int UiPluginQml::getFontWeightAdjustment() const { -#ifdef USE_CUSTOM_REGISTRATION - constexpr const char* cModuleName = "Governikus.Type"; - qmlRegisterModule(cModuleName, 1, 0); - - qmlRegisterUncreatableMetaObject(EnumUiModule::staticMetaObject, cModuleName, 1, 0, "UiModule", QStringLiteral("Not creatable as it is an enum type")); - qmlRegisterUncreatableMetaObject(EnumReaderManagerPluginType::staticMetaObject, cModuleName, 1, 0, "ReaderManagerPluginType", QStringLiteral("Not creatable as it is an enum type")); - qmlRegisterUncreatableMetaObject(EnumCardReturnCode::staticMetaObject, cModuleName, 1, 0, "CardReturnCode", QStringLiteral("Not creatable as it is an enum type")); - qmlRegisterUncreatableMetaObject(EnumGAnimation::staticMetaObject, cModuleName, 1, 0, "GAnimation", QStringLiteral("Not creatable as it is an enum type")); - - #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) - qmlRegisterType(cModuleName, 1, 0, "DiagnosisModel"); - qmlRegisterType(cModuleName, 1, 0, "SectionModel"); - #endif -#endif + return mFontWeightAdjustment; +} + + +void UiPluginQml::setFontWeightAdjustment(int pFontWeightAdjustment) +{ + if (pFontWeightAdjustment != mFontWeightAdjustment) + { + qCDebug(qml) << "System font weight adjustment has changed"; + mFontWeightAdjustment = pFontWeightAdjustment; + Q_EMIT fireFontWeightAdjustmentChanged(); + } } @@ -204,18 +175,11 @@ connect(mEngine.data(), &QQmlApplicationEngine::warnings, this, &UiPluginQml::onQmlWarnings, Qt::QueuedConnection); connect(mEngine.data(), &QQmlApplicationEngine::objectCreated, this, &UiPluginQml::onQmlObjectCreated, Qt::QueuedConnection); - UiPluginQml::registerQmlTypes(); - mEngine->addImportPath(QStringLiteral("qrc:///qml/")); -#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0)) - mEngine->addImportPath(QStringLiteral("qrc:///qt/qml/")); - mEngine->load(QStringLiteral("qrc:///qt/qml/Governikus/Init/App.qml")); -#else - #ifndef QT_NO_DEBUG +#ifndef QT_NO_DEBUG adjustQmlImportPath(mEngine.data()); - #endif - mEngine->loadFromModule(QAnyStringView("Governikus.Init"), QAnyStringView("App")); #endif + mEngine->loadFromModule(QAnyStringView("Governikus.Init"), QAnyStringView("App")); QQuickWindow* rootWindow = getRootWindow(); if (rootWindow != nullptr) @@ -228,9 +192,7 @@ #if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) rootWindow->resize(getInitialWindowSize()); #endif -#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) - setOsDarkMode(qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark); -#endif + setOsDarkMode(QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark); } onWindowPaletteChanged(); @@ -260,14 +222,9 @@ { qCDebug(qml) << "Override platform:" << platform; auto importPath = pEngine->importPathList(); - QMutableListIterator iter(importPath); - while (iter.hasNext()) - { - if (iter.next() == primaryPrefix) - { - iter.remove(); - } - } + erase_if(importPath, [primaryPrefix](const auto& pEntry){ + return pEntry == primaryPrefix; + }); primaryPrefix = QStringLiteral("qrc:/%1").arg(platform); importPath << primaryPrefix; pEngine->setImportPathList(importPath); @@ -556,20 +513,6 @@ } -bool UiPluginQml::showUpdateInformationIfPending() -{ - if (!isUpdatePending()) - { - return false; - } - - setUpdatePending(false); - onShowUi(UiModule::UPDATEINFORMATION); - - return true; -} - - bool UiPluginQml::eventFilter(QObject* pObj, QEvent* pEvent) { if (pEvent->type() == QEvent::ApplicationPaletteChange) @@ -578,13 +521,11 @@ return true; } -#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) if (pEvent->type() == QEvent::ThemeChange) { - setOsDarkMode(qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark); + setOsDarkMode(QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark); return true; } -#endif if (pEvent->type() == QEvent::FocusIn) { @@ -820,10 +761,6 @@ qCDebug(qml) << "Dark mode setting has changed"; mDarkMode = pState; -#ifdef Q_OS_WIN - updateTitleBarColor(getRootWindow(), pState); -#endif - onUserDarkModeChanged(); Q_EMIT fireDarkModeEnabledChanged(); @@ -831,18 +768,6 @@ } -bool UiPluginQml::isOsDarkModeSupported() const -{ -#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) - return true; - -#else - return false; - -#endif -} - - bool UiPluginQml::isDarkModeEnabled() const { const auto userDarkMode = Env::getSingleton()->getDarkMode(); @@ -894,8 +819,6 @@ void UiPluginQml::setScaleFactor(qreal pScaleFactor) { - pScaleFactor *= DEFAULT_SCALE_FACTOR; - if (qAbs(pScaleFactor - mScaleFactor) > 0) { mScaleFactor = pScaleFactor; @@ -999,4 +922,72 @@ void UiPluginQml::onAppConfigChanged() { setFontScaleFactor(getSystemFontScaleFactor()); + setFontWeightAdjustment(getSystemFontWeightAdjustment()); + setA11yButtonShapeActive(getA11yButtonShapeActive()); + setA11yOnOffSwitchLabelActive(getA11yOnOffSwitchLabelActive()); + onUserDarkModeChanged(); +} + + +void UiPluginQml::onReaderStatusChanged(const ReaderManagerPluginInfo& pInfo) const +{ +#if defined(Q_OS_IOS) + if (pInfo.getPluginType() != ReaderManagerPluginType::NFC) + { + return; + } + + QQuickWindow* rootWindow = getRootWindow(); + if (!rootWindow) + { + return; + } + + if (!pInfo.isScanRunning()) + { + rootWindow->reportContentOrientationChange(Qt::PrimaryOrientation); + return; + } + + if (QScreen* screen = rootWindow->screen(); screen) + { + rootWindow->reportContentOrientationChange(screen->orientation()); + } +#else + Q_UNUSED(pInfo) +#endif +} + + +void UiPluginQml::setA11yButtonShapeActive(bool pActive) +{ + if (pActive != mA11yButtonShapeActive) + { + qCDebug(qml) << "A11y button shape changed to" << pActive; + mA11yButtonShapeActive = pActive; + Q_EMIT fireA11yButtonShapeActiveChanged(); + } +} + + +bool UiPluginQml::isA11yButtonShapeActive() const +{ + return mA11yButtonShapeActive; +} + + +void UiPluginQml::setA11yOnOffSwitchLabelActive(bool pActive) +{ + if (pActive != mA11yOnOffSwitchLabelActive) + { + qCDebug(qml) << "A11y on off switch label changed to" << pActive; + mA11yOnOffSwitchLabelActive = pActive; + Q_EMIT fireA11yOnOffSwitchLabelActiveChanged(); + } +} + + +bool UiPluginQml::isA11yOnOffSwitchLabelActive() const +{ + return mA11yOnOffSwitchLabelActive; } diff -Nru ausweisapp2-2.3.1/src/ui/qml/UiPluginQml.h ausweisapp2-2.4.0/src/ui/qml/UiPluginQml.h --- ausweisapp2-2.3.1/src/ui/qml/UiPluginQml.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/UiPluginQml.h 2025-10-30 10:10:48.000000000 +0000 @@ -4,6 +4,7 @@ #pragma once +#include "ReaderManager.h" #include "TrayIcon.h" #include "UiPluginModel.h" @@ -15,7 +16,7 @@ #endif #ifdef Q_OS_IOS -Q_FORWARD_DECLARE_OBJC_CLASS(FontChangeTracker); +Q_FORWARD_DECLARE_OBJC_CLASS(SystemSettingsTracker); #endif @@ -44,25 +45,32 @@ bool mHighContrastEnabled; bool mDarkMode; bool mShowFocusIndicator; - constexpr static qreal DEFAULT_SCALE_FACTOR = 0.6; qreal mScaleFactor; qreal mFontScaleFactor; + int mFontWeightAdjustment; + bool mA11yButtonShapeActive; + bool mA11yOnOffSwitchLabelActive; void init(); [[nodiscard]] static QString getOverridePlatform(); [[nodiscard]] QQuickWindow* getRootWindow() const; [[nodiscard]] bool isHidden() const; - [[nodiscard]] bool showUpdateInformationIfPending(); [[nodiscard]] qreal getSystemFontScaleFactor() const; void setFontScaleFactor(qreal pFactor); + [[nodiscard]] int getSystemFontWeightAdjustment() const; + void setFontWeightAdjustment(int pFontWeightAdjustment); void setOsDarkMode(bool pState); + [[nodiscard]] bool getA11yButtonShapeActive() const; + void setA11yButtonShapeActive(bool pActive); + [[nodiscard]] bool getA11yOnOffSwitchLabelActive() const; + void setA11yOnOffSwitchLabelActive(bool pActive); #ifdef Q_OS_IOS struct Private { Private(); ~Private(); - FontChangeTracker* const mFontChangeTracker; + SystemSettingsTracker* const mSystemSettingsTracker; }; const QScopedPointer mPrivate; #endif @@ -74,7 +82,6 @@ UiPluginQml(); ~UiPluginQml() override = default; - static void registerQmlTypes(); #ifndef QT_NO_DEBUG static QString adjustQmlImportPath(QQmlEngine* pEngine); #endif @@ -87,7 +94,6 @@ [[nodiscard]] QVariantMap getSafeAreaMargins() const override; [[nodiscard]] bool isHighContrastEnabled() const override; [[nodiscard]] bool isOsDarkModeEnabled() const; - [[nodiscard]] bool isOsDarkModeSupported() const override; [[nodiscard]] bool isDarkModeEnabled() const override; [[nodiscard]] QString getFixedFontFamily() const override; [[nodiscard]] QSize getInitialWindowSize() const override; @@ -95,7 +101,10 @@ [[nodiscard]] qreal getScaleFactor() const override; void setScaleFactor(qreal pScaleFactor) override; [[nodiscard]] qreal getFontScaleFactor() const override; + [[nodiscard]] int getFontWeightAdjustment() const override; [[nodiscard]] bool isChromeOS() const override; + [[nodiscard]] bool isA11yButtonShapeActive() const override; + [[nodiscard]] bool isA11yOnOffSwitchLabelActive() const override; Q_INVOKABLE void hideFromTaskbar() const override; Q_INVOKABLE void doRefresh() override; @@ -131,6 +140,7 @@ void onUseSystemFontChanged() const; void onTrayIconEnabledChanged(); void onAppConfigChanged(); + void onReaderStatusChanged(const ReaderManagerPluginInfo& pInfo) const; }; } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/ui/qml/UiPluginQml_android.cpp ausweisapp2-2.4.0/src/ui/qml/UiPluginQml_android.cpp --- ausweisapp2-2.3.1/src/ui/qml/UiPluginQml_android.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/UiPluginQml_android.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -16,6 +16,25 @@ using namespace governikus; +QJniObject getAndroidResources_helper() +{ + QJniObject ctx = QNativeInterface::QAndroidApplication::context(); + if (!ctx.isValid()) + { + qCWarning(qml) << "Android context is invalid" << ctx.toString(); + return QJniObject(); + } + auto rsc = ctx.callObjectMethod( + "getResources", + "()Landroid/content/res/Resources;"); + if (!rsc.isValid()) + { + qCWarning(qml) << "Android resources are invalid" << rsc.toString(); + return QJniObject(); + } + return rsc; +} + extern "C" { @@ -34,24 +53,9 @@ qreal UiPluginQml::getSystemFontScaleFactor() const { - QJniObject ctx = QNativeInterface::QAndroidApplication::context(); - if (!ctx.isValid()) - { - qCWarning(qml) << "Android context is invalid" << ctx.toString(); - return 1.0; - } - auto rsc = ctx.callObjectMethod( - "getResources", - "()Landroid/content/res/Resources;"); + QJniObject rsc = getAndroidResources_helper(); if (!rsc.isValid()) { - qCWarning(qml) << "Android resources are invalid" << rsc.toString(); - return 1.0; - } - auto cfg = rsc.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;"); - if (!cfg.isValid()) - { - qCWarning(qml) << "Android configuration is invalid" << cfg.toString(); return 1.0; } auto displayMetrics = rsc.callObjectMethod("getDisplayMetrics", "()Landroid/util/DisplayMetrics;"); @@ -66,8 +70,30 @@ } +int UiPluginQml::getSystemFontWeightAdjustment() const +{ + if (QJniObject::getStaticField("android/os/Build$VERSION", "SDK_INT") < 31) // Android 12 + { + return 0; + } + QJniObject rsc = getAndroidResources_helper(); + if (!rsc.isValid()) + { + return 0; + } + auto cfg = rsc.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;"); + if (!cfg.isValid()) + { + qCWarning(qml) << "Android configuration is invalid" << cfg.toString(); + return 0; + } + return cfg.getField("fontWeightAdjustment"); +} + + } + void UiPluginQml::onUserDarkModeChanged() const { if (QJniObject activity = QNativeInterface::QAndroidApplication::context(); activity.isValid()) @@ -75,3 +101,15 @@ activity.callMethod("setAppearanceLightStatusBars", "(Z)V", !isDarkModeEnabled()); } } + + +bool UiPluginQml::getA11yButtonShapeActive() const +{ + return false; +} + + +bool UiPluginQml::getA11yOnOffSwitchLabelActive() const +{ + return false; +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/UiPluginQml_generic.cpp ausweisapp2-2.4.0/src/ui/qml/UiPluginQml_generic.cpp --- ausweisapp2-2.3.1/src/ui/qml/UiPluginQml_generic.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/UiPluginQml_generic.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -14,3 +14,21 @@ { return 1.0; } + + +int UiPluginQml::getSystemFontWeightAdjustment() const +{ + return 0; +} + + +bool UiPluginQml::getA11yButtonShapeActive() const +{ + return false; +} + + +bool UiPluginQml::getA11yOnOffSwitchLabelActive() const +{ + return false; +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/UiPluginQml_ios.mm ausweisapp2-2.4.0/src/ui/qml/UiPluginQml_ios.mm --- ausweisapp2-2.3.1/src/ui/qml/UiPluginQml_ios.mm 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/UiPluginQml_ios.mm 2025-10-30 10:10:48.000000000 +0000 @@ -18,12 +18,12 @@ using namespace Qt::Literals::StringLiterals; using namespace governikus; -@interface FontChangeTracker +@interface SystemSettingsTracker : NSObject - (instancetype) init; - (void) receiveNotification: (NSNotification*) notification; @end -@implementation FontChangeTracker +@implementation SystemSettingsTracker - (instancetype) init { self = [super init]; @@ -36,15 +36,35 @@ selector:@selector(receiveNotification:) name:UIContentSizeCategoryDidChangeNotification object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(receiveNotification:) + name:UIAccessibilityBoldTextStatusDidChangeNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(receiveNotification:) + name:UIAccessibilityButtonShapesEnabledStatusDidChangeNotification + object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(receiveNotification:) + name:UIAccessibilityOnOffSwitchLabelsDidChangeNotification + object:nil]; + return self; } - (void)receiveNotification:(NSNotification*)notification { - if ([notification.name - isEqualToString: - UIContentSizeCategoryDidChangeNotification]) + if ([notification.name isEqualToString:UIContentSizeCategoryDidChangeNotification] || + [notification.name isEqualToString:UIAccessibilityBoldTextStatusDidChangeNotification] || + [notification.name isEqualToString:UIAccessibilityButtonShapesEnabledStatusDidChangeNotification] || + [notification.name isEqualToString:UIAccessibilityOnOffSwitchLabelsDidChangeNotification]) { QMetaObject::invokeMethod(QCoreApplication::instance(), [] { if (auto* uiPlugin = Env::getSingleton()->getLoaded()) @@ -58,13 +78,14 @@ @end + @interface QIOSViewController : UIViewController @property (nonatomic, assign) UIStatusBarStyle preferredStatusBarStyle; @end -UiPluginQml::Private::Private() : mFontChangeTracker([[FontChangeTracker alloc] init]) +UiPluginQml::Private::Private() : mSystemSettingsTracker([[SystemSettingsTracker alloc] init]) { } @@ -105,6 +126,12 @@ } +int UiPluginQml::getSystemFontWeightAdjustment() const +{ + return UIAccessibilityIsBoldTextEnabled() ? 300 : 0; +} + + void UiPluginQml::onUserDarkModeChanged() const { UIWindow* window = PlatformTools::getFirstWindow(); @@ -125,3 +152,15 @@ qtViewController.preferredStatusBarStyle = isDarkModeEnabled() ? UIStatusBarStyleLightContent : UIStatusBarStyleDarkContent; [qtViewController setNeedsStatusBarAppearanceUpdate]; } + + +bool UiPluginQml::getA11yButtonShapeActive() const +{ + return UIAccessibilityButtonShapesEnabled(); +} + + +bool UiPluginQml::getA11yOnOffSwitchLabelActive() const +{ + return UIAccessibilityIsOnOffSwitchLabelsEnabled(); +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/UiPluginQml_osx.mm ausweisapp2-2.4.0/src/ui/qml/UiPluginQml_osx.mm --- ausweisapp2-2.3.1/src/ui/qml/UiPluginQml_osx.mm 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/UiPluginQml_osx.mm 2025-10-30 10:10:48.000000000 +0000 @@ -26,3 +26,21 @@ { return 1.0; } + + +int UiPluginQml::getSystemFontWeightAdjustment() const +{ + return 0; +} + + +bool UiPluginQml::getA11yButtonShapeActive() const +{ + return false; +} + + +bool UiPluginQml::getA11yOnOffSwitchLabelActive() const +{ + return false; +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/VersionInformationModel.cpp ausweisapp2-2.4.0/src/ui/qml/VersionInformationModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/VersionInformationModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/VersionInformationModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -37,7 +37,7 @@ mData.clear(); BuildHelper::processInformationHeader([this](const QString& pKey, const QString& pValue){ - mData += qMakePair(pKey, pValue); + mData += std::make_pair(pKey, pValue); }); } @@ -50,7 +50,7 @@ QVariant VersionInformationModel::data(const QModelIndex& pIndex, int pRole) const { - if (pIndex.isValid() && pIndex.row() < rowCount()) + if (pIndex.isValid() && pIndex.row() < rowCount(pIndex)) { const auto& [key, value] = std::as_const(mData).at(pIndex.row()); if (pRole == KEY) diff -Nru ausweisapp2-2.3.1/src/ui/qml/VersionInformationModel.h ausweisapp2-2.4.0/src/ui/qml/VersionInformationModel.h --- ausweisapp2-2.3.1/src/ui/qml/VersionInformationModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/VersionInformationModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -9,10 +9,11 @@ #include #include -#include #include #include +#include + namespace governikus { @@ -33,7 +34,7 @@ KEY = Qt::UserRole + 1, VALUE }; - QList> mData; + QList> mData; VersionInformationModel(); ~VersionInformationModel() override = default; @@ -41,7 +42,7 @@ void init(); public: - [[nodiscard]] int rowCount(const QModelIndex& = QModelIndex()) const override; + [[nodiscard]] int rowCount(const QModelIndex&) const override; [[nodiscard]] QVariant data(const QModelIndex& pIndex, int pRole = Qt::DisplayRole) const override; [[nodiscard]] QHash roleNames() const override; }; diff -Nru ausweisapp2-2.3.1/src/ui/qml/WorkflowModel.cpp ausweisapp2-2.4.0/src/ui/qml/WorkflowModel.cpp --- ausweisapp2-2.3.1/src/ui/qml/WorkflowModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/WorkflowModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -271,6 +271,13 @@ } +QString WorkflowModel::getStatusCodeDisplayString() const +{ + //: LABEL ALL_PLATFORMS + return tr("Error code: %1").arg(getEnumName(getStatusCode())); +} + + GAnimation WorkflowModel::getStatusCodeAnimation() const { switch (getStatusCode()) @@ -404,15 +411,6 @@ } -void WorkflowModel::setRemoveCardFeedback(bool pEnabled) -{ - if (mContext) - { - mContext->setRemoveCardFeedback(pEnabled); - } -} - - void WorkflowModel::setInitialPluginType() { #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) @@ -494,7 +492,7 @@ Q_ASSERT(mContext); QString mailSubject = getEmailHeader(); QString mailBody = getEmailBody(true, true); - QString url = QStringLiteral("mailto:support@ausweisapp.de?subject=%1&body=%2").arg(mailSubject, mailBody); + const auto url = QUrl(QStringLiteral("mailto:support@ausweisapp.de?subject=%1&body=%2").arg(mailSubject, mailBody)); QDesktopServices::openUrl(url); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/WorkflowModel.h ausweisapp2-2.4.0/src/ui/qml/WorkflowModel.h --- ausweisapp2-2.3.1/src/ui/qml/WorkflowModel.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/WorkflowModel.h 2025-10-30 10:10:48.000000000 +0000 @@ -49,11 +49,13 @@ Q_PROPERTY(bool isCurrentSmartCardAllowed READ isCurrentSmartCardAllowed NOTIFY fireIsCurrentSmartCardAllowedChanged) Q_PROPERTY(QString eidTypeMismatchError READ eidTypeMismatchError NOTIFY fireEidTypeMismatchErrorChanged) Q_PROPERTY(bool hasNextWorkflowPending READ getNextWorkflowPending NOTIFY fireNextWorkflowPendingChanged) + Q_PROPERTY(governikus::EnumGlobalStatusCode::GlobalStatusCode statusCode READ getStatusCode NOTIFY fireResultChanged) + Q_PROPERTY(QString statusCodeDisplayString READ getStatusCodeDisplayString NOTIFY fireResultChanged) Q_PROPERTY(QString statusHintText READ getStatusHintText NOTIFY fireResultChanged) Q_PROPERTY(QString statusHintTitle READ getStatusHintTitle NOTIFY fireResultChanged) Q_PROPERTY(QString statusHintActionText READ getStatusHintActionText NOTIFY fireResultChanged) Q_PROPERTY(governikus::EnumGAnimation::GAnimation statusCodeAnimation READ getStatusCodeAnimation NOTIFY fireResultChanged) - Q_PROPERTY(bool showRemoveCardFeedback READ showRemoveCardFeedback WRITE setRemoveCardFeedback NOTIFY fireRemoveCardFeedbackChanged) + Q_PROPERTY(bool showRemoveCardFeedback READ showRemoveCardFeedback NOTIFY fireRemoveCardFeedbackChanged) Q_PROPERTY(bool cardInitiallyAppeared READ getCardInitiallyAppeared NOTIFY fireHasCardChanged) Q_PROPERTY(bool hasCard READ hasCard NOTIFY fireHasCardChanged) Q_PROPERTY(governikus::EnumCardReturnCode::CardReturnCode lastReturnCode READ getLastReturnCode NOTIFY fireLastReturnCodeChanged) @@ -95,6 +97,7 @@ [[nodiscard]] bool getNextWorkflowPending() const; [[nodiscard]] GlobalStatus::Code getStatusCode() const; + [[nodiscard]] QString getStatusCodeDisplayString() const; [[nodiscard]] virtual GAnimation getStatusCodeAnimation() const; [[nodiscard]] QString getStatusHintText() const; @@ -103,7 +106,6 @@ [[nodiscard]] Q_INVOKABLE bool invokeStatusHintAction(); [[nodiscard]] bool showRemoveCardFeedback() const; - void setRemoveCardFeedback(bool pEnabled); Q_INVOKABLE void insertSmartCard() const; Q_INVOKABLE void insertSimulator() const; diff -Nru ausweisapp2-2.3.1/src/ui/qml/metadata.json ausweisapp2-2.4.0/src/ui/qml/metadata.json --- ausweisapp2-2.3.1/src/ui/qml/metadata.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/metadata.json 2025-10-30 10:10:48.000000000 +0000 @@ -2,10 +2,6 @@ "default": true, "userInteractive": true, "env": { - "QSG_INFO": "1", - "QT_SVG_ASSUME_TRUSTED_SOURCE": "1" - }, - "_comments": [ - "QT_SVG_ASSUME_TRUSTED_SOURCE is only required with Qt < 6.8.0" - ] + "QSG_INFO": "1" + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Animations/+6.5/HourglassAnimation.qml ausweisapp2-2.4.0/src/ui/qml/modules/Animations/+6.5/HourglassAnimation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Animations/+6.5/HourglassAnimation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Animations/+6.5/HourglassAnimation.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -/** - * Copyright (c) 2023-2025 Governikus GmbH & Co. KG, Germany - */ -import Governikus.Global -import Governikus.Style - -TintableIcon { - source: "qrc:///animations/hourglass.svg" - sourceSize.height: Style.dimens.header_icon_size - tintColor: Style.color.image -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Animations/AnimationLoader.qml ausweisapp2-2.4.0/src/ui/qml/modules/Animations/AnimationLoader.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Animations/AnimationLoader.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Animations/AnimationLoader.qml 2025-10-30 10:10:48.000000000 +0000 @@ -34,53 +34,53 @@ property bool animated: true property int symbol: Symbol.Type.STAR - property int type: AnimationLoader.NONE + property int type: AnimationLoader.Type.NONE sourceComponent: { switch (type) { - case AnimationLoader.REMOTE_PIN: + case AnimationLoader.Type.REMOTE_PIN: return remotePin; - case AnimationLoader.TRANSPORT_PIN: + case AnimationLoader.Type.TRANSPORT_PIN: return transportPin; - case AnimationLoader.PIN: + case AnimationLoader.Type.PIN: return pin; - case AnimationLoader.PIN_UNKNOWN: + case AnimationLoader.Type.PIN_UNKNOWN: return pinUnknown; - case AnimationLoader.NEW_PIN: + case AnimationLoader.Type.NEW_PIN: return newPin; - case AnimationLoader.CAN: + case AnimationLoader.Type.CAN: return can; - case AnimationLoader.PUK: + case AnimationLoader.Type.PUK: return puk; - case AnimationLoader.WAIT_FOR_CARD_SAC: + case AnimationLoader.Type.WAIT_FOR_CARD_SAC: return waitForCardSac; - case AnimationLoader.WAIT_FOR_CARD_USB: + case AnimationLoader.Type.WAIT_FOR_CARD_USB: return waitForCardUsb; - case AnimationLoader.WAIT_FOR_READER: + case AnimationLoader.Type.WAIT_FOR_READER: return waitForReader; - case AnimationLoader.WAIT_FOR_SAC: + case AnimationLoader.Type.WAIT_FOR_SAC: return waitForSac; - case AnimationLoader.NFC_RESULT: + case AnimationLoader.Type.NFC_RESULT: return nfcResult; - case AnimationLoader.SAC_RESULT: + case AnimationLoader.Type.SAC_RESULT: return sacResult; - case AnimationLoader.CHANGEPIN_SUCCESS: + case AnimationLoader.Type.CHANGEPIN_SUCCESS: return changePinSuccess; - case AnimationLoader.SAC_CONNECTION: + case AnimationLoader.Type.SAC_CONNECTION: return sacConnection; - case AnimationLoader.CARD_RESULT: + case AnimationLoader.Type.CARD_RESULT: return cardResult; - case AnimationLoader.NETWORK_ERROR: + case AnimationLoader.Type.NETWORK_ERROR: return networkError; - case AnimationLoader.STATUS: + case AnimationLoader.Type.STATUS: return statusAnimation; - case AnimationLoader.HOURGLASS: + case AnimationLoader.Type.HOURGLASS: return hourglassAnimation; default: return undefined; } } - visible: (status === Loader.Ready || status === Loader.Loading) && type !== AnimationLoader.NONE + visible: (status === Loader.Ready || status === Loader.Loading) && type !== AnimationLoader.Type.NONE Component { id: remotePin diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Animations/HourglassAnimation.qml ausweisapp2-2.4.0/src/ui/qml/modules/Animations/HourglassAnimation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Animations/HourglassAnimation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Animations/HourglassAnimation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -12,7 +12,7 @@ TintableIcon { id: root - readonly property bool animated: SettingsModel.useAnimations && GraphicsInfo.api !== GraphicsInfo.Software + readonly property bool animated: SettingsModel.useAnimations && GraphicsInfo.api !== GraphicsInfo.Software && visible source: animated ? "qrc:///animations/hourglass_background.svg" : "qrc:///animations/hourglass.svg" sourceSize.height: Style.dimens.header_icon_size diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Animations/LinkQualityAnimation.qml ausweisapp2-2.4.0/src/ui/qml/modules/Animations/LinkQualityAnimation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Animations/LinkQualityAnimation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Animations/LinkQualityAnimation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -17,6 +17,12 @@ property var percent property int size: Style.dimens.icon_size + Accessible.name: inactive ? + //: INFO ALL_PLATFORMS + qsTr("Link quality unavailable.") : + //: INFO ALL_PLATFORMS %1 is replaced with a number between 0 and 100 + qsTr("%1% link quality.").arg(percent) + Accessible.role: Accessible.StaticText implicitHeight: size implicitWidth: size diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Animations/SpinningLoader.qml ausweisapp2-2.4.0/src/ui/qml/modules/Animations/SpinningLoader.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Animations/SpinningLoader.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Animations/SpinningLoader.qml 2025-10-30 10:10:48.000000000 +0000 @@ -12,8 +12,8 @@ Item { id: root - implicitHeight: Style.dimens.progress_bar_height - implicitWidth: Style.dimens.progress_bar_height + implicitHeight: 2 * Style.dimens.text + implicitWidth: implicitHeight Repeater { id: repeater diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Animations/WorkflowAnimationLoader.qml ausweisapp2-2.4.0/src/ui/qml/modules/Animations/WorkflowAnimationLoader.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Animations/WorkflowAnimationLoader.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Animations/WorkflowAnimationLoader.qml 2025-10-30 10:10:48.000000000 +0000 @@ -25,22 +25,22 @@ switch (animation) { case GAnimation.STATUS_OK: case GAnimation.STATUS_ERROR: - return AnimationLoader.STATUS; + return AnimationLoader.Type.STATUS; case GAnimation.PIN_ERROR: - return AnimationLoader.PIN; + return AnimationLoader.Type.PIN; case GAnimation.CAN_ERROR: - return AnimationLoader.CAN; + return AnimationLoader.Type.CAN; case GAnimation.PUK_ERROR: case GAnimation.PUK_BLOCKED: - return AnimationLoader.PUK; + return AnimationLoader.Type.PUK; case GAnimation.CARD_ERROR: - return AnimationLoader.CARD_RESULT; + return AnimationLoader.Type.CARD_RESULT; case GAnimation.NETWORK_ERROR: - return AnimationLoader.NETWORK_ERROR; + return AnimationLoader.Type.NETWORK_ERROR; case GAnimation.CHANGEPIN_SUCCESS: - return AnimationLoader.CHANGEPIN_SUCCESS; + return AnimationLoader.Type.CHANGEPIN_SUCCESS; default: - return AnimationLoader.NONE; + return AnimationLoader.Type.NONE; } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+desktop/EditRights.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+desktop/EditRights.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+desktop/EditRights.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+desktop/EditRights.qml 2025-10-30 10:10:48.000000000 +0000 @@ -21,9 +21,6 @@ fillWidth: true spacing: Style.dimens.pane_spacing - Keys.onEnterPressed: confirmButton.clicked() - Keys.onReturnPressed: confirmButton.clicked() - RowLayout { Layout.alignment: Qt.AlignHCenter Layout.maximumWidth: Style.dimens.max_text_width * 1.2 @@ -170,7 +167,7 @@ CertificateDescriptionPage { titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+desktop/TransportPinReminderView.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+desktop/TransportPinReminderView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+desktop/TransportPinReminderView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+desktop/TransportPinReminderView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,7 +14,7 @@ id: root titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Cancel + navigationAction: NavigationAction.Action.Cancel onNavigationActionClicked: root.cancel() } @@ -37,7 +37,7 @@ MultiInfoView { titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } @@ -50,7 +50,7 @@ title: root.title titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+desktop/internal/AuthController.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+desktop/internal/AuthController.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+desktop/internal/AuthController.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+desktop/internal/AuthController.qml 2025-10-30 10:10:48.000000000 +0000 @@ -29,8 +29,10 @@ } readonly property bool isInitialState: workflowState === AuthController.WorkflowStates.Initial + readonly property bool isSelfAuth: ApplicationModel.currentWorkflow === ApplicationModel.Workflow.SELF_AUTHENTICATION readonly property alias networkInterfaceActive: connectivityManager.networkInterfaceActive property bool startedByOnboarding: false + property bool userCancelAndManualRedirect: false property bool workflowProgressVisible: false property int workflowState: 0 @@ -80,6 +82,13 @@ AuthModel.continueWorkflow(); } break; + case "StateCertificateDescriptionCheck": + if (ApplicationModel.screenReaderRunning && SettingsModel.autoRedirectAfterAuthentication && SettingsModel.remindUserOfAutoRedirect) { + push(a11yAutoRedirectDecision); + } else { + AuthModel.continueWorkflow(); + } + break; case "StateEditAccessRights": if (NumberModel.isCanAllowedMode && SettingsModel.skipRightsOnCanAllowed) { ChatModel.transferAccessRights(); @@ -126,23 +135,29 @@ push(cardPositionView); break; case "StateSendDIDAuthenticateResponseEAC1": + userCancelAndManualRedirect = false; if (AuthModel.isCancellationByUser()) { - push(authAbortedProgressView); + if (SettingsModel.autoRedirectAfterAuthentication) { + push(regularAbortedAuthView); + } else { + userCancelAndManualRedirect = true; + push(confirmAbortedAuthView); + if (root.isSelfAuth) { + //For a SelfAuth we skip continue otherwise we run staight into FinalState and leave + break; + } + } } else { workflowProgressVisible = true; pop(root); } setAuthWorkflowStateAndContinue(AuthController.WorkflowStates.Processing); break; - case "StateActivateStoreFeedbackDialog": - if (ApplicationModel.currentWorkflow === ApplicationModel.Workflow.SELF_AUTHENTICATION) { - showRemoveCardFeedback(AuthModel, true); - } - AuthModel.continueWorkflow(); - break; case "StateRedirectBrowser": if (!AuthModel.error) { push(redirectToProvider); + } else if (userCancelAndManualRedirect) { + break; } else { AuthModel.continueWorkflow(); } @@ -157,16 +172,15 @@ } else { backToStartPage(false); } + } else if (userCancelAndManualRedirect) { + userCancelledAndBackToStart(true); } else { - showRemoveCardFeedback(AuthModel, false); push(authResult); } - } else if (ApplicationModel.currentWorkflow === ApplicationModel.Workflow.SELF_AUTHENTICATION) { + } else if (root.isSelfAuth) { push(selfAuthData); } else { - pop(root); - backToStartPage(AuthModel.isCancellationByUser()); - AuthModel.continueWorkflow(); + userCancelledAndBackToStart(false); } workflowProgressVisible = false; break; @@ -190,6 +204,11 @@ AuthModel.continueWorkflow(); } } + function userCancelledAndBackToStart(pUserCancelled) { + pop(root); + backToStartPage(pUserCancelled); + AuthModel.continueWorkflow(); + } //: LABEL DESKTOP headline: (AuthModel.error ? qsTr("Cancel authentication process") : @@ -197,6 +216,8 @@ isInitialState ? qsTr("Acquiring provider certificate") : //: INFO DESKTOP Header of the progress status message during the authentication process. qsTr("Authentication in progress")) + //: LABEL DESKTOP Name of an progress indicator during an authentication read by screen readers + progressBarA11yText: qsTr("Authentication progress") progressBarVisible: workflowProgressVisible progressText: AuthModel.progressMessage progressValue: AuthModel.progressValue @@ -240,14 +261,26 @@ ConnectivityManager { id: connectivityManager - watching: true + watching: AuthModel.currentState === "StateGetTcToken" onNetworkInterfaceActiveChanged: { - if (AuthModel.currentState === "StateGetTcToken") + if (watching) root.rerunCurrentState(); } } Component { + id: a11yAutoRedirectDecision + + AutoRedirectDecision { + title: root.title + + onLeaveView: { + root.pop(); + AuthModel.continueWorkflow(); + } + } + } + Component { id: checkConnectivityView ProgressView { @@ -310,7 +343,7 @@ } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Cancel + navigationAction: NavigationAction.Action.Cancel navigationEnabled: AuthModel.isBasicReader showSettings: true @@ -393,7 +426,7 @@ ResultView { animationSymbol: Symbol.Type.ERROR - animationType: AnimationLoader.NFC_RESULT + animationType: AnimationLoader.Type.NFC_RESULT text: AuthModel.isRemoteReader ? //: INFO DESKTOP 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. qsTr("Weak NFC signal. Please\n change the card position\n remove the mobile phone case (if present)\n connect the smartphone with a charging cable") : @@ -409,13 +442,12 @@ } } Component { - id: authAbortedProgressView + id: regularAbortedAuthView ProgressView { //: INFO DESKTOP The user aborted the authentication process, according to TR we need to inform the service provider headline: qsTr("Aborting process and informing the service provider") - progressBarVisible: false text: { if (connectivityManager.networkInterfaceActive) { //: INFO DESKTOP Information message about cancellation process with present network connectivity @@ -429,6 +461,23 @@ } } Component { + id: confirmAbortedAuthView + + AuthCanceledView { + startedByOnboarding: root.startedByOnboarding + title: root.title + + titleBarSettings: TitleBarSettings { + navigationAction: !root.isSelfAuth ? NavigationAction.Action.Cancel : root.startedByOnboarding ? NavigationAction.Action.Back : NavigationAction.Action.Close + navigationEnabled: root.isSelfAuth + + onNavigationActionClicked: AuthModel.continueWorkflow() + } + + onLeaveView: AuthModel.continueWorkflow() + } + } + Component { id: selfAuthData SelfAuthenticationData { @@ -440,12 +489,15 @@ title: root.title titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: root.startedByOnboarding ? NavigationAction.Action.Back : NavigationAction.Action.Close onNavigationActionClicked: { root.pop(); AuthModel.continueWorkflow(); - root.backToSelfAuthView(); + if (navigationAction === NavigationAction.Action.Back) + root.backToSelfAuthView(); + else + root.backToStartPage(true); } } @@ -473,6 +525,8 @@ id: authResult ResultView { + id: authResultView + animation: AuthModel.statusCodeAnimation buttonIcon: AuthModel.resultViewButtonIcon buttonText: root.startedByOnboarding ? @@ -482,14 +536,19 @@ hintButtonText: AuthModel.statusHintActionText hintText: AuthModel.statusHintText hintTitle: AuthModel.statusHintTitle + linkToOpen: AuthModel.resultViewButtonLink mailButtonVisible: AuthModel.errorIsMasked popupText: AuthModel.errorText - //: INFO DESKTOP Error code (string) of current GlobalStatus code, shown as header of popup. - popupTitle: qsTr("Error code: %1").arg(AuthModel.statusCodeString) + popupTitle: AuthModel.statusCodeDisplayString subheader: AuthModel.errorHeader text: AuthModel.resultString title: root.title - titleBarSettings: disabledCancelNavigation + + titleBarSettings: TitleBarSettings { + navigationAction: root.startedByOnboarding ? NavigationAction.Action.Back : NavigationAction.Action.Close + + onNavigationActionClicked: authResultView.leaveView() + } onEmailButtonPressed: AuthModel.sendResultMail() onHintClicked: AuthModel.invokeStatusHintAction() @@ -505,24 +564,21 @@ TitleBarSettings { id: disabledCancelNavigation - navigationAction: NavigationAction.Cancel + navigationAction: NavigationAction.Action.Cancel navigationEnabled: false } TitleBarSettings { id: cancelNavigation - navigationAction: NavigationAction.Cancel + navigationAction: NavigationAction.Action.Cancel navigationEnabled: AuthModel.isBasicReader - onNavigationActionClicked: { - root.pop(root); - AuthModel.cancelWorkflow(); - } + onNavigationActionClicked: AuthModel.cancelWorkflow() } TitleBarSettings { id: backNavigation - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+desktop/internal/DataGroup.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+desktop/internal/DataGroup.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+desktop/internal/DataGroup.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+desktop/internal/DataGroup.qml 2025-10-30 10:10:48.000000000 +0000 @@ -21,9 +21,16 @@ property alias titleStyle: dataTitle.textStyle property bool writeAccess: false + Accessible.focusable: true + Accessible.ignored: title === "" + Accessible.name: title + Accessible.role: Accessible.Grouping spacing: Style.dimens.pane_spacing visible: count > 0 + onFocusChanged: if (focus) + Utils.positionViewAtItem(this) + GText { id: dataTitle @@ -85,10 +92,10 @@ FocusFrame { anchors { - bottomMargin: Style.dimens.separator_size * 2 + bottomMargin: 2 * Style.dimens.border_width leftMargin: 0 rightMargin: 0 - topMargin: Style.dimens.separator_size + topMargin: Style.dimens.border_width } } } @@ -108,10 +115,10 @@ FocusFrame { anchors { - bottomMargin: Style.dimens.separator_size * 2 + bottomMargin: Style.dimens.border_width * 2 leftMargin: 0 rightMargin: 0 - topMargin: Style.dimens.separator_size + topMargin: Style.dimens.border_width } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+desktop/internal/SelfAuthenticationData.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+desktop/internal/SelfAuthenticationData.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+desktop/internal/SelfAuthenticationData.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+desktop/internal/SelfAuthenticationData.qml 2025-10-30 10:10:48.000000000 +0000 @@ -7,7 +7,6 @@ import QtQuick import QtQuick.Layouts -import Governikus.Animations import Governikus.Global import Governikus.Style import Governikus.View @@ -22,12 +21,6 @@ margins: Style.dimens.pane_padding * 2 spacing: Style.dimens.pane_spacing - //: LABEL DESKTOP Title of the self authentication result data view - title: qsTr("Read self-authentication data") - - Keys.onEnterPressed: okButton.clicked() - Keys.onReturnPressed: okButton.clicked() - Connections { function onFireCancelWorkflow() { if (SelfAuthModel.workflowCancelled) { @@ -38,24 +31,8 @@ enabled: root.visible target: SelfAuthModel } - Row { - id: statusRow - - Layout.preferredHeight: root.height / 4 - spacing: Style.dimens.pane_spacing - - StatusAnimation { - anchors.verticalCenter: parent.verticalCenter - sourceSize.height: Style.dimens.huge_icon_size - symbol.type: Symbol.Type.CHECK - } - GText { - anchors.verticalCenter: parent.verticalCenter - - //: INFO DESKTOP Status message that the self authentication successfully completed. - text: qsTr("Successfully read data") - textStyle: Style.text.headline - } + SelfAuthenticationHeader { + iconSize: Style.dimens.large_icon_size } GPane { id: pane diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/AbortedProgressView.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/AbortedProgressView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/AbortedProgressView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/AbortedProgressView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,7 +14,6 @@ //: INFO DESKTOP The user aborted the authentication process, according to TR we need to inform the service provider headline: qsTr("Aborting process and informing the service provider") - progressBarVisible: false text: { if (networkInterfaceActive) { //: INFO DESKTOP Information message about cancellation process with present network connectivity diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/CardPositionView.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/CardPositionView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/CardPositionView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/CardPositionView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,7 +14,7 @@ id: root animationSymbol: Symbol.Type.ERROR - animationType: AnimationLoader.NFC_RESULT + animationType: AnimationLoader.Type.NFC_RESULT //: LABEL ANDROID IOS buttonText: qsTr("Retry") //: LABEL ANDROID IOS diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/EditRights.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/EditRights.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/EditRights.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/EditRights.qml 2025-10-30 10:10:48.000000000 +0000 @@ -76,6 +76,7 @@ qsTr("By entering your PIN, access to the following data of your ID card will be allowed to the mentioned provider:") } GPane { + Accessible.ignored: true Layout.fillWidth: true color: Style.color.paneSublevel.background.basic drawShadow: false @@ -88,13 +89,11 @@ text: root.workflowModel.transactionInfo textFormat: Text.StyledText visible: !!text - width: parent.width } GText { //: LABEL IOS_PHONE ANDROID_PHONE text: qsTr("The provider mentioned above does not require any data stored on your ID card, only confirmation of you possessing a valid ID card.") visible: !writeData.visible && !readData.visible - width: parent.width } } GPane { @@ -108,12 +107,12 @@ DataGroup { id: writeData + Layout.fillWidth: true chat: ChatModel.write //: LABEL IOS_PHONE ANDROID_PHONE title: qsTr("Write access (update)") titleStyle: Style.text.headline - width: parent.width writeAccess: true onScrollPageDown: root.scrollPageDown() @@ -125,18 +124,18 @@ Layout.fillWidth: true color: Style.color.paneSublevel.background.basic + contentPadding: 0 drawShadow: false - padding: 0 visible: requiredData.count > 0 || optionalData.count > 0 DataGroup { id: requiredData + Layout.fillWidth: true chat: ChatModel.required //: LABEL IOS_PHONE ANDROID_PHONE title: qsTr("Read access") - width: parent.width onScrollPageDown: root.scrollPageDown() onScrollPageUp: root.scrollPageUp() @@ -144,11 +143,11 @@ DataGroup { id: optionalData + Layout.fillWidth: true chat: ChatModel.optional //: LABEL IOS_PHONE ANDROID_PHONE title: qsTr("Read access (optional)") - width: parent.width onScrollPageDown: root.scrollPageDown() onScrollPageUp: root.scrollPageUp() diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/TransportPinReminderView.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/TransportPinReminderView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/TransportPinReminderView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/TransportPinReminderView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany */ pragma ComponentBehavior: Bound diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/internal/AuthController.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/internal/AuthController.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/internal/AuthController.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/internal/AuthController.qml 2025-10-30 10:10:48.000000000 +0000 @@ -23,11 +23,13 @@ property bool hideTechnologySwitch: false property var initialPlugin: null property bool isInitialState: true + readonly property bool isSelfAuth: ApplicationModel.currentWorkflow === ApplicationModel.Workflow.SELF_AUTHENTICATION readonly property bool isSmartWorkflow: AuthModel.readerPluginType === ReaderManagerPluginType.SMART readonly property int passwordType: NumberModel.passwordType readonly property bool smartEidUsed: isSmartWorkflow && !isInitialState property bool startedByOnboarding: false property string title + property bool userCancelAndManualRedirect: false property bool workflowProgressVisible: false signal changeTransportPin @@ -71,6 +73,13 @@ AuthModel.continueWorkflow(); } break; + case "StateCertificateDescriptionCheck": + if (ApplicationModel.screenReaderRunning && SettingsModel.autoRedirectAfterAuthentication && SettingsModel.remindUserOfAutoRedirect) { + push(a11yAutoRedirectDecision); + } else { + AuthModel.continueWorkflow(); + } + break; case "StateEditAccessRights": if (NumberModel.isCanAllowedMode && SettingsModel.skipRightsOnCanAllowed) { ChatModel.transferAccessRights(); @@ -131,14 +140,18 @@ AuthModel.continueWorkflow(); break; case "StateSendDIDAuthenticateResponseEAC1": + userCancelAndManualRedirect = false; if (AuthModel.isCancellationByUser()) { - replace(authAbortedProgressView); - } - AuthModel.continueWorkflow(); - break; - case "StateActivateStoreFeedbackDialog": - if (ApplicationModel.currentWorkflow === ApplicationModel.Workflow.SELF_AUTHENTICATION) { - showRemoveCardFeedback(AuthModel, true); + if (SettingsModel.autoRedirectAfterAuthentication) { + push(regularAbortedAuthView); + } else { + userCancelAndManualRedirect = true; + push(confirmAbortedAuthView); + if (ApplicationModel.currentWorkflow === ApplicationModel.Workflow.SELF_AUTHENTICATION) { + //For a SelfAuth we skip continue otherwise we run staight into FinalState and leave + break; + } + } } AuthModel.continueWorkflow(); break; @@ -155,6 +168,8 @@ case "StateRedirectBrowser": if (!AuthModel.error) { replace(redirectToProvider); + } else if (userCancelAndManualRedirect) { + break; } else { AuthModel.continueWorkflow(); } @@ -163,8 +178,9 @@ if (AuthModel.changeTransportPin) { AuthModel.continueWorkflow(); changeTransportPin(); + } else if (userCancelAndManualRedirect) { + AuthModel.continueWorkflow(); } else if (AuthModel.error && !AuthModel.hasNextWorkflowPending && !AuthModel.shouldSkipResultView()) { - showRemoveCardFeedback(AuthModel, false); replace(authResult); } else { AuthModel.continueWorkflow(); @@ -202,14 +218,26 @@ ConnectivityManager { id: connectivityManager - watching: true + watching: AuthModel.currentState === "StateGetTcToken" onNetworkInterfaceActiveChanged: { - if (AuthModel.currentState === "StateGetTcToken") + if (watching) root.rerunCurrentState(); } } Component { + id: a11yAutoRedirectDecision + + AutoRedirectDecision { + title: root.title + + onLeaveView: { + root.pop(); + AuthModel.continueWorkflow(); + } + } + } + Component { id: progressView ProgressView { @@ -219,6 +247,8 @@ root.isInitialState ? qsTr("Acquiring provider certificate") : //: INFO ANDROID IOS Header of the progress status message during the authentication process. qsTr("Authentication in progress")) + //: LABEL ANDROID IOS Name of an progress indicator during an authentication read by screen readers + progressBarA11yText: qsTr("Authentication progress") progressBarVisible: root.workflowProgressVisible progressText: AuthModel.progressMessage progressValue: AuthModel.progressValue @@ -268,8 +298,17 @@ qsTr("Back to start page") smartEidUsed: root.smartEidUsed - onDone: AuthModel.continueWorkflow() - onRequestBack: root.requestBack() + navigationAction: NavigationAction { + action: root.startedByOnboarding ? NavigationAction.Action.Back : NavigationAction.Action.Close + + onClicked: { + if (root.startedByOnboarding) + root.requestBack(); + AuthModel.continueWorkflow(); + } + } + + onLeaveView: AuthModel.continueWorkflow() } } Component { @@ -356,7 +395,7 @@ } } Component { - id: authAbortedProgressView + id: regularAbortedAuthView AbortedProgressView { networkInterfaceActive: connectivityManager.networkInterfaceActive @@ -367,6 +406,24 @@ } } Component { + id: confirmAbortedAuthView + + AuthCanceledView { + smartEidUsed: root.smartEidUsed + startedByOnboarding: root.startedByOnboarding + title: root.title + + navigationAction: NavigationAction { + action: !root.isSelfAuth ? NavigationAction.Action.Cancel : root.startedByOnboarding ? NavigationAction.Action.Back : NavigationAction.Action.Close + enabled: root.isSelfAuth + + onClicked: AuthModel.continueWorkflow() + } + + onLeaveView: AuthModel.continueWorkflow() + } + } + Component { id: checkConnectivityView CheckConnectivityView { @@ -392,10 +449,12 @@ id: authResult ResultErrorView { + id: authResultView + animation: AuthModel.statusCodeAnimation //: LABEL ANDROID IOS buttonText: root.startedByOnboarding ? qsTr("Back to setup") : AuthModel.resultViewButtonText - errorCode: AuthModel.statusCodeString + errorCode: AuthModel.statusCodeDisplayString errorDescription: AuthModel.errorText //: LABEL ANDROID IOS header: qsTr("Authentication failed") @@ -409,12 +468,23 @@ text: AuthModel.resultString title: root.title + navigationAction: NavigationAction { + action: root.startedByOnboarding ? NavigationAction.Action.Back : NavigationAction.Action.Close + + onClicked: authResultView.continueClicked() + } + onContinueClicked: AuthModel.continueWorkflow() onHintClicked: { AuthModel.continueWorkflow(); AuthModel.invokeStatusHintAction(); } - onMailClicked: LogModel.mailLog("support@ausweisapp.de", AuthModel.getEmailHeader(), AuthModel.getEmailBody()) + onMailClicked: logModel.mailLogFile("support@ausweisapp.de", AuthModel.getEmailHeader(), AuthModel.getEmailBody()) + + LogModel { + id: logModel + + } } } Component { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/internal/DataGroup.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/internal/DataGroup.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/internal/DataGroup.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/internal/DataGroup.qml 2025-10-30 10:10:48.000000000 +0000 @@ -23,7 +23,6 @@ signal scrollPageUp visible: repeater.count > 0 - width: parent.width PaneTitle { id: dataTitle @@ -68,14 +67,15 @@ onFocusChanged: item.focus = focus // QTBUG-122734 GSeparator { - anchors.left: parent.left visible: !delegateLoader.isLast + z: 1 anchors { + bottom: parent.bottom + left: parent.left leftMargin: Style.dimens.pane_padding right: parent.right rightMargin: Style.dimens.pane_padding - top: parent.bottom } } Component { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/internal/SelfAuthenticationData.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/internal/SelfAuthenticationData.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/+mobile/internal/SelfAuthenticationData.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/+mobile/internal/SelfAuthenticationData.qml 2025-10-30 10:10:48.000000000 +0000 @@ -6,8 +6,8 @@ import QtQuick import QtQuick.Layouts + import Governikus.Global -import Governikus.TitleBar import Governikus.View import Governikus.Type import Governikus.Style @@ -17,22 +17,17 @@ property alias okButtonText: okButton.text - signal done - signal requestBack - spacing: Style.dimens.pane_spacing //: LABEL ANDROID IOS title: qsTr("Identify") - navigationAction: NavigationAction { - action: NavigationAction.Action.Back + GPane { + Layout.fillWidth: true - onClicked: { - root.requestBack(); - root.done(); + SelfAuthenticationHeader { + iconSize: Style.dimens.icon_size } } - GridLayout { id: grid @@ -66,6 +61,6 @@ //: LABEL ANDROID IOS text: qsTr("Back to start page") - onClicked: root.done() + onClicked: root.leaveView() } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/AuthCanceledView.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/AuthCanceledView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/AuthCanceledView.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/AuthCanceledView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick +import QtQuick.Layouts + +import Governikus.Animations +import Governikus.Global +import Governikus.Style +import Governikus.Type +import Governikus.View + +FlickableSectionPage { + id: root + + readonly property bool isSelfAuth: ApplicationModel.currentWorkflow === ApplicationModel.Workflow.SELF_AUTHENTICATION + property bool startedByOnboarding: false + + spacing: Style.dimens.pane_spacing + + Heading { + //: LABEL ALL_PLATFORMS + text: qsTr("Authentication was canceled") + visible: text !== "" + } + AnimationLoader { + Layout.alignment: Qt.AlignHCenter + symbol: Symbol.Type.ERROR + type: AnimationLoader.Type.STATUS + } + Subheading { + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + //: LABEL ALL_PLATFORMS + text: qsTr("The authentication is being canceled at the provider (%1)").arg(CertificateDescriptionModel.subjectName) + visible: text !== "" + } + GText { + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + //: LABEL ALL_PLATFORMS + text: qsTr("This may take a few moments.") + visible: text !== "" + } + GProgressBar { + //: LABEL ALL_PLATFORMS Name of an progress indicator during the cancellation of an authentication read by screen readers + Accessible.name: qsTr("Cancellation progress") + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.maximumWidth: Style.dimens.max_text_width + value: root.isSelfAuth ? 100 : AuthModel.currentState === "StateRedirectBrowser" ? 100 : 50 + } + GText { + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + text: root.isSelfAuth || AuthModel.currentState === "StateRedirectBrowser" ? + //: LABEL ALL_PLATFORMS + qsTr("The authentication was cancelled successfully.") + "
" + + //: LABEL ALL_PLATFORMS + (AuthModel.statusCode === GlobalStatusCode.Card_Cancellation_By_User ? qsTr("The cancellation was triggered on the card reader.") + "
" : "") + (Style.is_layout_desktop ? + //: LABEL DESKTOP + qsTr("You may start a new authentication after clicking the button.") : + //: LABEL ANDROID IOS + qsTr("You may start a new authentication after tapping the button.")) : "" + visible: text !== "" + } + GSpacer { + Layout.fillHeight: true + } + GButton { + Layout.alignment: Qt.AlignHCenter + enabled: root.isSelfAuth ? true : AuthModel.currentState === "StateRedirectBrowser" + icon.source: root.isSelfAuth ? "" : "qrc:///images/open_website.svg" + text: !root.isSelfAuth ? + //: LABEL ALL_PLATFORMS + qsTr("Back to provider") : root.startedByOnboarding ? + //: LABEL ALL_PLATFORM + qsTr("Back to setup") : + //: LABEL ALL_PLATFORMS + qsTr("Back to start page") + tintIcon: true + + onClicked: root.leaveView() + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/ProviderInfo.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/ProviderInfo.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/ProviderInfo.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/ProviderInfo.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,22 +1,22 @@ /** * Copyright (c) 2016-2025 Governikus GmbH & Co. KG, Germany */ + import QtQuick import QtQuick.Layouts -import QtQuick.Controls + import Governikus.Global import Governikus.Style import Governikus.View -import Governikus.Type -AbstractButton { +GAbstractButton { id: root required property string name //: LABEL DESKTOP - Accessible.description: Style.is_layout_desktop ? qsTr("Show more information about the service provider") : "" - Accessible.name: labelText.label + ". " + labelText.text + (Style.is_layout_desktop ? "" : (". " + link.text)) + Accessible.description: Style.is_layout_desktop ? qsTr("Show more information about the service provider") : link.text + Accessible.name: labelText.label + ". " + labelText.text Accessible.role: Accessible.Button padding: Style.dimens.pane_padding @@ -63,13 +63,12 @@ id: link Accessible.ignored: true - color: labelText.labelColor - font.pixelSize: UiPluginModel.scaleFactor * 24 text: Style.is_layout_desktop ? //: LABEL DESKTOP qsTr("Details about the provider") : //: LABEL ANDROID IOS - qsTr("Touch for more details") + qsTr("Tap for more details") + textStyle: Style.text.link } } TintableIcon { @@ -80,9 +79,6 @@ } } - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) - HoverHandler { id: hoverHandler diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/internal/AutoRedirectDecision.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/internal/AutoRedirectDecision.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/internal/AutoRedirectDecision.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/internal/AutoRedirectDecision.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import Governikus.Global +import Governikus.Type + +DecisionView { + id: root + + //: INFO ALL_PLATFORMS + agreeButtonText: qsTr("Enable manual redirection") + + //: INFO ALL_PLATFORMS + descriptionTextsModel: [qsTr("Currently, the app automatically redirects you back to the service provider after authentication. This may not allow your screen reader to provide all information. To ensure you receive all information, enable manual redirection to the service provider."), + //: INFO ALL_PLATFORMS + qsTr("You can change your preference at any time in the settings.")] + disagreeButtonHighlighted: false + + //: LABEL ALL_PLATFORMS + disagreeButtonText: qsTr("Skip and keep automatic redirection") + //: INFO ALL_PLATFORMS + headlineText: qsTr("Optimize your settings for screen reading") + + customContentSourceComponent: GCheckBox { + checked: !SettingsModel.remindUserOfAutoRedirect + horizontalPadding: 0 + maximumLineCount: 2 + //: LABEL ALL_PLATFORMS + text: qsTr("Do not display this message in future.") + + onCheckedChanged: SettingsModel.remindUserOfAutoRedirect = !checked + } + + onAgreeClicked: { + SettingsModel.autoRedirectAfterAuthentication = false; + root.leaveView(); + } + onDisagreeClicked: root.leaveView() +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/internal/BaseTransportPinReminderView.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/internal/BaseTransportPinReminderView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/internal/BaseTransportPinReminderView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/internal/BaseTransportPinReminderView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -25,14 +25,11 @@ MultiInfoData { id: infoData - contentType: MultiInfoData.CHANGE_PIN + contentType: MultiInfoData.Type.CHANGE_PIN } - GText { - Layout.alignment: Qt.AlignHCenter - + Heading { //: LABEL ALL_PLATFORMS text: qsTr("Set up card PIN") - textStyle: Style.text.headline } GText { Layout.alignment: Qt.AlignHCenter @@ -44,18 +41,17 @@ Layout.alignment: Qt.AlignHCenter text: infoData.linkText - onClicked: root.showInfoView(MultiInfoData.CHANGE_PIN) + onClicked: root.showInfoView(MultiInfoData.Type.CHANGE_PIN) } - GText { + Subheading { //: LABEL ALL_PLATFORMS text: qsTr("What kind of PIN do you have?") - textStyle: Style.text.subline } PinSelectionButtons { Layout.alignment: Qt.AlignHCenter onFiveDigitPin: root.showTransportPinInfo() - onNoPinAvailable: root.showInfoView(MultiInfoData.NO_PIN) + onNoPinAvailable: root.showInfoView(MultiInfoData.Type.NO_PIN) onSixDigitPin: root.pinKnown() } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/internal/RedirectView.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/internal/RedirectView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/internal/RedirectView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/internal/RedirectView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -15,12 +15,23 @@ //: LABEL ALL_PLATFORMS readonly property string leaveText: qsTr("If you have any questions or encounter any errors during the process, please contact the corresponding provider.") readonly property string redirectText: { + if (Style.is_layout_desktop) { + if (SettingsModel.autoRedirectAfterAuthentication) { + //: INFO DESKTOP Redirect information when automatic redirect is enabled + return qsTr("You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, click the \"%1\" button.").arg(buttonText); + } + + //: INFO DESKTOP Redirect information when automatic redirect is disabled + return qsTr("Click the button to complete the authentication and return to the provider."); + } + if (SettingsModel.autoRedirectAfterAuthentication) { - //: INFO ALL_PLATFORMS Redirect information when automatic redirect is enabled - return qsTr("You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, click on the \"%1\" button.").arg(buttonText); + //: INFO ANDROID IOS Redirect information when automatic redirect is enabled + return qsTr("You will be automatically redirected to the provider in a few seconds. If you are not automatically redirected, tap the \"%1\" button.").arg(buttonText); } - //: INFO ALL_PLATFORMS Redirect information when automatic redirect is disabled - return qsTr("Press the button to complete the authentication and return to the provider."); + + //: INFO ANDROID IOS Redirect information when automatic redirect is disabled + return qsTr("Tap the button to complete the authentication and return to the provider."); } animationSymbol: Symbol.Type.CHECK @@ -30,6 +41,7 @@ buttonText: qsTr("Return to provider") //: LABEL ALL_PLATFORMS header: qsTr("Authentication successful") + linkToOpen: AuthModel.refreshUrl subheader: { if (Style.is_layout_desktop) { //: INFO DESKTOP Hint to user that the ID card should be removed @@ -58,4 +70,11 @@ onTriggered: root.confirm() } + Connections { + function onFireAppAboutToQuit() { + timeout.stop(); + } + + target: ApplicationModel + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/internal/SelfAuthenticationHeader.qml ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/internal/SelfAuthenticationHeader.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/AuthView/internal/SelfAuthenticationHeader.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/AuthView/internal/SelfAuthenticationHeader.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick +import QtQuick.Layouts + +import Governikus.Animations +import Governikus.Global +import Governikus.Style + +RowLayout { + property alias iconSize: icon.sourceSize.height + + spacing: Style.dimens.pane_spacing + + StatusAnimation { + id: icon + + Layout.alignment: Qt.AlignTop + symbol.type: Symbol.Type.CHECK + } + GText { + //: INFO ALL_PLATFORMS Status message that the self authentication successfully completed (1/2). + text: qsTr("Successfully read data.") + "
" + + //: INFO ALL_PLATFORMS Status message that the self authentication successfully completed (2/2). + qsTr("You may now remove your ID card from the device.") + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/CMakeLists.txt ausweisapp2-2.4.0/src/ui/qml/modules/CMakeLists.txt --- ausweisapp2-2.3.1/src/ui/qml/modules/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -94,9 +94,6 @@ endforeach() if(CMAKE_SYSTEM_NAME STREQUAL "${_system}") - if(QT_VERSION VERSION_LESS "6.5") - set(RESOURCE_PREFIX RESOURCE_PREFIX /qt/qml) - endif() set(type general) set(path ${_system}/Governikus/${name}) set(import_path ${_system}) diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/+desktop/ChangePinController.qml ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/+desktop/ChangePinController.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/+desktop/ChangePinController.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/+desktop/ChangePinController.qml 2025-10-30 10:10:48.000000000 +0000 @@ -31,6 +31,7 @@ } property ProgressTracker baseProgressTracker: null + property Component cardNotActivatedDelegate: null property Component errorViewDelegate: null property alias navigationActionType: cancelNavigation.navigationAction property Component successViewDelegate: null @@ -123,14 +124,15 @@ setPinWorkflowStateAndContinue(ChangePinController.WorkflowStates.Processing); break; case "FinalState": - showRemoveCardFeedback(ChangePinModel, false); if (ChangePinModel.shouldSkipResultView()) { ChangePinModel.continueWorkflow(); pop(root); break; } d.setWorkflowProgress(progressTracker.steps); - if (ChangePinModel.error) { + if (ChangePinModel.error && ChangePinModel.statusCode === GlobalStatusCode.Card_Pin_Deactivated) { + push(root.cardNotActivatedDelegate ? root.cardNotActivatedDelegate : cardNotActivatedView); + } else if (ChangePinModel.error) { push(root.errorViewDelegate ? root.errorViewDelegate : pinResult); } else { push(root.successViewDelegate ? root.successViewDelegate : pinResult); @@ -333,7 +335,7 @@ progress: progressTracker titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } @@ -362,7 +364,7 @@ id: cardPositionView ResultView { - animation: AnimationLoader.NFC_RESULT + animation: AnimationLoader.Type.NFC_RESULT animationSymbol: Symbol.Type.ERROR progress: progressTracker text: ChangePinModel.isRemoteReader ? @@ -383,6 +385,8 @@ id: pinResult ResultView { + id: pinResultView + animation: ChangePinModel.statusCodeAnimation //: LABEL DESKTOP buttonText: qsTr("Back to start page") @@ -395,8 +399,9 @@ title: root.title titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Cancel - navigationEnabled: false + navigationAction: NavigationAction.Action.Close + + onNavigationActionClicked: pinResultView.leaveView() } onEmailButtonPressed: ChangePinModel.sendResultMail() @@ -407,10 +412,26 @@ } } } + Component { + id: cardNotActivatedView + + CardNotActivatedView { + title: root.title + + titleBarSettings: TitleBarSettings { + navigationAction: NavigationAction.Action.Close + + onNavigationActionClicked: { + ChangePinModel.continueWorkflow(); + root.pop(root); + } + } + } + } TitleBarSettings { id: cancelNavigation - navigationAction: NavigationAction.Cancel + navigationAction: NavigationAction.Action.Cancel navigationEnabled: ChangePinModel.isBasicReader onNavigationActionClicked: { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/+desktop/ChangePinView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/+desktop/ChangePinView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/+desktop/ChangePinView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/+desktop/ChangePinView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -16,8 +16,9 @@ id: root property bool activateUI: true + property Component cardNotActivatedDelegate: null property Component errorViewDelegate: null - property int navigationActionType: NavigationAction.Cancel + property int navigationActionType: NavigationAction.Action.Cancel property bool onlyCheckPin: false property bool skipInfoView: true property Component successViewDelegate: null @@ -30,7 +31,7 @@ title: qsTr("Change PIN") titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } @@ -53,7 +54,7 @@ } } onChangePinInfoRequested: root.push(multiInfoView, { - passwordType: MultiInfoData.CHANGE_PIN, + passwordType: MultiInfoData.Type.CHANGE_PIN, progress: progressTracker }) onChangeTransportPin: { @@ -66,7 +67,7 @@ } } onNoPinAvailable: root.push(multiInfoView, { - passwordType: MultiInfoData.NO_PIN, + passwordType: MultiInfoData.Type.NO_PIN, progress: noPinProgress, continueButtonText: root.usedInOnboarding ? qsTr("Abort setup") : "" }) @@ -84,6 +85,7 @@ ChangePinController { required property bool changeTransportPin + cardNotActivatedDelegate: root.cardNotActivatedDelegate errorViewDelegate: root.errorViewDelegate navigationActionType: root.navigationActionType successViewDelegate: root.successViewDelegate @@ -114,7 +116,7 @@ contentType: infoView.passwordType } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: root.titleBarSettings.startEnabled onNavigationActionClicked: root.pop() @@ -141,7 +143,7 @@ title: root.title titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: root.titleBarSettings.startEnabled onNavigationActionClicked: root.pop() @@ -161,7 +163,7 @@ title: root.title titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: root.titleBarSettings.startEnabled onNavigationActionClicked: root.pop() diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/+mobile/ChangePinController.qml ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/+mobile/ChangePinController.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/+mobile/ChangePinController.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/+mobile/ChangePinController.qml 2025-10-30 10:10:48.000000000 +0000 @@ -21,6 +21,7 @@ id: root property bool autoInsertCard: false + property Component cardNotActivatedDelegate: null property Component errorViewDelegate: null property bool hideTechnologySwitch: false property var initialPlugin: null @@ -48,9 +49,15 @@ continueWorkflowIfComfortReader(); } function displaySuccessView(pPasswordType) { - push(inputSuccessView, { - passwordType: pPasswordType - }); + if (stackView.currentItem instanceof InputSuccessView) { + replace(inputSuccessView, { + passwordType: pPasswordType + }); + } else { + push(inputSuccessView, { + passwordType: pPasswordType + }); + } continueWorkflowIfComfortReader(); } function processStateChange(pState) { @@ -97,18 +104,18 @@ case "StateUnfortunateCardPosition": push(cardPositionView); break; - case "StatePrepareChangePin": + case "StateEnterNewPacePin": d.setWorkflowProgress(3); - if (stackView.currentItem instanceof InputSuccessView) { - pop(); - } if (ChangePinModel.requestTransportPin && !root.isNewPin) { - displaySuccessView(NumberModel.PasswordType.TRANSPORT_PIN); - return; + switch (NumberModel.passwordType) { + case NumberModel.PasswordType.NEW_PIN_CONFIRMATION: + case NumberModel.PasswordType.NEW_SMART_PIN_CONFIRMATION: + break; + default: + displaySuccessView(NumberModel.PasswordType.TRANSPORT_PIN); + return; + } } - ChangePinModel.continueWorkflow(); - break; - case "StateEnterNewPacePin": if (NumberModel.inputError !== "") { displayInputError(); NumberModel.newPin = ""; @@ -118,13 +125,15 @@ requestInput(); break; case "FinalState": - showRemoveCardFeedback(ChangePinModel, false); if (ChangePinModel.shouldSkipResultView()) { ChangePinModel.continueWorkflow(); break; } d.setWorkflowProgress(progressTracker.steps); - if (ChangePinModel.error) { + if (ChangePinModel.error && ChangePinModel.statusCode === GlobalStatusCode.Card_Pin_Deactivated) { + push(root.cardNotActivatedDelegate ? root.cardNotActivatedDelegate : cardNotActivatedView); + break; + } else if (ChangePinModel.error) { push(root.errorViewDelegate ? root.errorViewDelegate : pinResult); break; } @@ -263,16 +272,8 @@ } onContinueClicked: { - pop(); - switch (passwordType) { - case NumberModel.PasswordType.CAN: - case NumberModel.PasswordType.PUK: - root.requestInput(); - break; - case NumberModel.PasswordType.TRANSPORT_PIN: - root.pop(); - ChangePinModel.continueWorkflow(); - } + root.pop(); + root.requestInput(); } } } @@ -280,6 +281,8 @@ id: pinResult ResultView { + id: pinResultView + animation: ChangePinModel.statusCodeAnimation //: LABEL ANDROID IOS buttonText: qsTr("Back to start page") @@ -292,11 +295,11 @@ title: root.title navigationAction: NavigationAction { - action: root.navigationActionType - enabled: false + action: NavigationAction.Action.Close + + onClicked: pinResultView.continueClicked() } - onCancelClicked: continueClicked() onContinueClicked: ChangePinModel.continueWorkflow() onHintClicked: { ChangePinModel.continueWorkflow(); @@ -362,9 +365,9 @@ break; case NumberModel.PasswordType.NEW_PIN_CONFIRMATION: case NumberModel.PasswordType.NEW_SMART_PIN_CONFIRMATION: + root.isNewPin = true; if (NumberModel.commitNewPin()) { d.setWorkflowProgress(5); - root.isNewPin = true; ChangePinModel.continueWorkflow(); } else { root.rerunCurrentState(); @@ -421,4 +424,18 @@ } } } + Component { + id: cardNotActivatedView + + CardNotActivatedView { + progress: root.progress + title: root.title + + navigationAction: NavigationAction { + action: NavigationAction.Action.Cancel + + onClicked: ChangePinModel.continueWorkflow() + } + } + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/+mobile/ChangePinView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/+mobile/ChangePinView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/+mobile/ChangePinView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/+mobile/ChangePinView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -17,6 +17,7 @@ property bool activateUI: true property bool autoInsertCard: false + property Component cardNotActivatedDelegate: null property Component errorViewDelegate: null property bool hidePinTypeSelection: false property bool hideTechnologySwitch: false @@ -81,6 +82,7 @@ ChangePinController { autoInsertCard: root.autoInsertCard + cardNotActivatedDelegate: root.cardNotActivatedDelegate errorViewDelegate: root.errorViewDelegate hideTechnologySwitch: root.hideTechnologySwitch initialPlugin: root.initialPlugin diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/ChangePinViewContent.qml ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/ChangePinViewContent.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/ChangePinViewContent.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/ChangePinViewContent.qml 2025-10-30 10:10:48.000000000 +0000 @@ -23,15 +23,13 @@ maximumContentWidth: Style.dimens.max_text_width spacing: Style.dimens.pane_spacing - GText { + Heading { id: pinDescWhatType - Layout.alignment: Qt.AlignHCenter Layout.topMargin: root.titleTopMargin //: LABEL ALL_PLATFORMS text: qsTr("What kind of PIN do you have?") - textStyle: Style.text.headline wrapMode: Text.WordWrap } MoreInformationLink { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/ChangeTransportPinInfoView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/ChangeTransportPinInfoView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/ChangeTransportPinInfoView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/ChangeTransportPinInfoView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -17,17 +17,10 @@ signal continueClicked - Keys.onEnterPressed: root.continueClicked() - Keys.onEscapePressed: root.leaveView() - Keys.onReturnPressed: root.continueClicked() - - GText { - Layout.alignment: Qt.AlignHCenter + Heading { Layout.bottomMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter //: LABEL ALL_PLATFORMS text: qsTr("5-digit Transport PIN") - textStyle: Style.text.headline } AnimationLoader { id: animation @@ -35,12 +28,11 @@ Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: Style.dimens.pane_spacing animated: false - type: AnimationLoader.TRANSPORT_PIN + type: AnimationLoader.Type.TRANSPORT_PIN } - GText { + Subheading { //: LABEL ALL_PLATFORMS text: qsTr("Have your documents ready") - textStyle: Style.text.subline } GText { Layout.bottomMargin: Style.dimens.text_spacing diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/internal/ChangePinInfoView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/internal/ChangePinInfoView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ChangePinView/internal/ChangePinInfoView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ChangePinView/internal/ChangePinInfoView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -17,17 +17,10 @@ signal continueClicked - Keys.onEnterPressed: root.continueClicked() - Keys.onEscapePressed: root.leaveView() - Keys.onReturnPressed: root.continueClicked() - - GText { - Layout.alignment: Qt.AlignHCenter + Heading { Layout.bottomMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter //: LABEL ALL_PLATFORMS text: qsTr("6-digit card PIN") - textStyle: Style.text.headline } AnimationLoader { id: animation @@ -35,12 +28,11 @@ Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: Style.dimens.pane_spacing animated: false - type: AnimationLoader.PIN + type: AnimationLoader.Type.PIN } - GText { + Subheading { //: LABEL ALL_PLATFORMS text: qsTr("Have your documents ready") - textStyle: Style.text.subline } GText { Layout.bottomMargin: Style.dimens.text_spacing diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardNoNfcSuggestion.qml ausweisapp2-2.4.0/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardNoNfcSuggestion.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardNoNfcSuggestion.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardNoNfcSuggestion.qml 2025-10-30 10:10:48.000000000 +0000 @@ -43,7 +43,7 @@ iconSourceComponent: AnimationLoader { animated: false symbol: Symbol.Type.ERROR - type: AnimationLoader.NFC_RESULT + type: AnimationLoader.Type.NFC_RESULT } onAgreeClicked: root.connectSmartphone() diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardView.qml ausweisapp2-2.4.0/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -152,10 +152,9 @@ sourceSize.height: Style.dimens.header_icon_size tintColor: Style.color.image } - GText { + Subheading { //: LABEL ANDROID IOS text: qsTr("Check if your device & ID card are ready for use") - textStyle: Style.text.subline } GText { //: LABEL ANDROID IOS diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardWorkflow.qml ausweisapp2-2.4.0/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardWorkflow.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardWorkflow.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/CheckIDCardView/+mobile/CheckIDCardWorkflow.qml 2025-10-30 10:10:48.000000000 +0000 @@ -80,9 +80,14 @@ onCancelClicked: root.cancel() onContinueClicked: { - if (checkIDCardModel.result === CheckIDCardModel.Result.SUCCESS) { + switch (checkIDCardModel.result) { + case CheckIDCardModel.Result.PIN_DEACTIVATED: + root.push(cardNotActivatedView); + break; + case CheckIDCardModel.Result.SUCCESS: root.checkSuccess(); - } else { + break; + default: root.push(checkIDCardSuggestionView, { result: checkIDCardModel.result }); @@ -91,6 +96,23 @@ } } Component { + id: cardNotActivatedView + + CardNotActivatedView { + continueButtonVisible: root.usedInOnboarding + progress: progressTracker + title: root.title + + navigationAction: NavigationAction { + action: NavigationAction.Action.Back + + onClicked: root.pop() + } + + onContinueClicked: root.pinDeactivated() + } + } + Component { id: checkIDCardSuggestionView CheckIDCardSuggestionView { @@ -105,7 +127,6 @@ onCancelClicked: root.cancel() onCheckSuccess: root.checkSuccess() - onPinDeactivated: root.pinDeactivated() onRestartCheck: { root.pop(root); root.restartCheck(); diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/CheckIDCardView/CheckIDCardResultView.qml ausweisapp2-2.4.0/src/ui/qml/modules/CheckIDCardView/CheckIDCardResultView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/CheckIDCardView/CheckIDCardResultView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/CheckIDCardView/CheckIDCardResultView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -10,6 +10,7 @@ import Governikus.Animations import Governikus.CheckResultView import Governikus.Global +import Governikus.Style import Governikus.Type CheckResultView { @@ -20,15 +21,15 @@ animationSymbol: d.success ? Symbol.Type.CHECK : Symbol.Type.ERROR animationType: { if (d.isPcsc) { - return AnimationLoader.CARD_RESULT; + return AnimationLoader.Type.CARD_RESULT; } if (d.isNfc) { - return AnimationLoader.NFC_RESULT; + return AnimationLoader.Type.NFC_RESULT; } if (d.isRemote) { - return AnimationLoader.SAC_RESULT; + return AnimationLoader.Type.SAC_RESULT; } - return AnimationLoader.NONE; + return AnimationLoader.Type.NONE; } buttonIcon: { if (d.successNfc) { @@ -62,10 +63,13 @@ title: qsTr("Check device and ID card") GText { - font.weight: Font.Bold + font.weight: Style.font.bold horizontalAlignment: Text.AlignHCenter - //: LABEL ALL_PLATFORMS - text: (d.successNfc && !root.usedInOnboarding) ? qsTr("You may now try the function: \"See my personal data\". Press the \"%1\" button to do so now.").arg(root.buttonText) : "" + text: (d.successNfc && !root.usedInOnboarding) ? (Style.is_layout_desktop ? + //: LABEL DESKTOP + qsTr("You may now try the function: \"See my personal data\". Click the \"%1\" button to do so now.") : + //: LABEL ANDROID IOS + qsTr("You may now try the function: \"See my personal data\". Tap the \"%1\" button to do so now.")).arg(root.buttonText) : "" visible: text !== "" } ObjectModel { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/CheckIDCardView/CheckIDCardSuggestionView.qml ausweisapp2-2.4.0/src/ui/qml/modules/CheckIDCardView/CheckIDCardSuggestionView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/CheckIDCardView/CheckIDCardSuggestionView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/CheckIDCardView/CheckIDCardSuggestionView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -9,10 +9,10 @@ id: root required property int result + readonly property string supportedDevicesLink: "https://www.ausweisapp.bund.de/%1/aa2/mobile-devices".arg(SettingsModel.language) property bool usedInOnboarding: false signal checkSuccess - signal pinDeactivated signal restartCheck Accessible.name: suggestionData.title @@ -24,8 +24,6 @@ return insufficientApduLengthSuggestionData; case CheckIDCardModel.Result.CARD_ACCESS_FAILED: return cardAccessFailedSuggestionData; - case CheckIDCardModel.Result.PIN_DEACTIVATED: - return pinDeactivatedSuggestionData; case CheckIDCardModel.Result.PIN_SUSPENDED: return pinSuspendedSuggestionData; case CheckIDCardModel.Result.PIN_BLOCKED: @@ -63,59 +61,37 @@ continueButtonIcon: "qrc:///images/open_website.svg" //: LABEL ALL_PLATFORMS continueButtonText: qsTr("Open website") + linkToOpen: root.supportedDevicesLink //: LABEL ALL_PLATFORMS text: qsTr("The NFC interface of your mobile device does not support Extended Length communication and cannot be used to read the ID card. Unfortunately, the %1 has no influence on this restriction.

You can find smartphones compatible with the %1 on our website.").arg(Qt.application.name) //: LABEL ALL_PLATFORMS title: qsTr("No extended length") - onContinueClicked: Qt.openUrlExternally("https://www.ausweisapp.bund.de/%1/aa2/mobile-devices".arg(SettingsModel.language)) + onContinueClicked: Qt.openUrlExternally(root.supportedDevicesLink) } SuggestionData { id: cardAccessFailedSuggestionData - readonly property string deviceUrl: "https://www.ausweisapp.bund.de/%1/aa2/mobile-devices".arg(SettingsModel.language) - continueButtonIcon: "qrc:///images/device_button.svg" //: LABEL ALL_PLATFORMS continueButtonText: qsTr("Retry") //: LABEL ALL_PLATFORMS - text: qsTr("It was not possible to establish a stable connection with your ID card.

Please start the check again. Try a different card position and make sure not to move the card during the test.

If a connection to the ID card cannot be established even with different card positions, this indicates that the NFC interface of your mobile device cannot supply the ID card with sufficient power.

Smartphones compatible with %1 can be found on our website.").arg(Qt.application.name).arg(deviceUrl) + text: qsTr("It was not possible to establish a stable connection with your ID card.

Please start the check again. Try a different card position and make sure not to move the card during the test.

If a connection to the ID card cannot be established even with different card positions, this indicates that the NFC interface of your mobile device cannot supply the ID card with sufficient power.

Smartphones compatible with %1 can be found on our website.").arg(Qt.application.name).arg(root.supportedDevicesLink) //: LABEL ALL_PLATFORMS title: qsTr("ID card access failed") onContinueClicked: root.restartCheck() } SuggestionData { - id: pinDeactivatedSuggestionData - - //: LABEL ALL_PLATFORMS - continueButtonText: root.usedInOnboarding ? qsTr("Abort setup") : "" - hintButtonText: PinResetInformationModel.pinResetActionText - hintText: PinResetInformationModel.activateOnlineFunctionHint - //: LABEL ALL_PLATFORMS Hint when a workflow failed because the eID function was not activated - hintTitle: qsTr("Activate the eID function.") - text: PinResetInformationModel.activateOnlineFunctionDescription - - //: LABEL ALL_PLATFORMS - title: qsTr("eID function disabled") - - onContinueClicked: { - if (root.usedInOnboarding) { - root.pinDeactivated(); - } - } - onHintClicked: Qt.openUrlExternally(PinResetInformationModel.pinResetUrl) - } - SuggestionData { id: pinSuspendedSuggestionData continueButtonIcon: "qrc:///images/identify.svg" //: LABEL ALL_PLATFORMS Sentence 1 of 3 of CAN explanation text: qsTr("The ID card PIN has been entered incorrectly 2 times in a row. This is why you must first enter the 6-digit Card Access Number (CAN) for the next identification process. You can find it at the bottom right of the front of your ID card.") + "

" + //: LABEL ALL_PLATFORMS Sentence 2 of 3 of CAN explanation - (root.usedInOnboarding ? qsTr("You may continue the onboarding and change your PIN.") : + (root.usedInOnboarding ? qsTr("You may continue the setup and change your PIN.") : //: LABEL ALL_PLATFORMS Sentence 2 of 3 of CAN explanation qsTr("You may now try the function: \"See my personal data\".")) + " " + //: LABEL ALL_PLATFORMS Sentence 3 of 3 of CAN explanation @@ -130,12 +106,13 @@ id: pinBlockedSuggestionData continueButtonIcon: "qrc:///images/identify.svg" + hintButtonLink: PinResetInformationModel.pinResetUrl hintButtonText: PinResetInformationModel.pinResetActionText hintText: PinResetInformationModel.noPinAndNoPukHint //: LABEL ALL_PLATFORMS Sentence 1 of 3 of PUK explanation text: qsTr("The ID card PIN has been entered incorrectly 3 times. Therefore, you must first enter the 10-digit PUK during the next authentication process. You can find it in the PIN letter you received after applying for your ID card.") + "

" + //: LABEL ALL_PLATFORMS Sentence 2 of 3 of PUK explanation - (root.usedInOnboarding ? qsTr("You may continue the onboarding and change your PIN.") : + (root.usedInOnboarding ? qsTr("You may continue the setup and change your PIN.") : //: LABEL ALL_PLATFORMS Sentence 2 of 3 of PUK explanation qsTr("You may now try the function: \"See my personal data\".")) + " " + //: LABEL ALL_PLATFORMS Sentence 3 of 3 of PUK explanation @@ -145,6 +122,5 @@ title: qsTr("ID card PIN blocked") onContinueClicked: root.checkSuccess() - onHintClicked: Qt.openUrlExternally(PinResetInformationModel.pinResetUrl) } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/CheckResultView/CheckResultSuggestionView.qml ausweisapp2-2.4.0/src/ui/qml/modules/CheckResultView/CheckResultSuggestionView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/CheckResultView/CheckResultSuggestionView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/CheckResultView/CheckResultSuggestionView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,9 +14,11 @@ buttonIcon: suggestionData.continueButtonIcon buttonText: suggestionData.continueButtonText header: suggestionData.header + hintButtonLink: suggestionData.hintButtonLink hintButtonText: suggestionData.hintButtonText hintText: suggestionData.hintText hintTitle: suggestionData.hintTitle + linkToOpen: suggestionData.linkToOpen text: suggestionData.text textFormat: suggestionData.textFormat title: suggestionData.title diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/CheckResultView/SuggestionData.qml ausweisapp2-2.4.0/src/ui/qml/modules/CheckResultView/SuggestionData.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/CheckResultView/SuggestionData.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/CheckResultView/SuggestionData.qml 2025-10-30 10:10:48.000000000 +0000 @@ -13,10 +13,12 @@ //: LABEL ALL_PLATFORMS property string continueButtonText: qsTr("Continue") property string header + property string hintButtonLink property string hintButtonText property string hintText //: LABEL ALL_PLATFORMS property string hintTitle: qsTr("Hint") + property string linkToOpen property string text property int textFormat: Text.AutoText property string title diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/EnterPasswordView/EnterPasswordView.qml ausweisapp2-2.4.0/src/ui/qml/modules/EnterPasswordView/EnterPasswordView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/EnterPasswordView/EnterPasswordView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/EnterPasswordView/EnterPasswordView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -2,6 +2,8 @@ * Copyright (c) 2016-2025 Governikus GmbH & Co. KG, Germany */ +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Layouts @@ -25,9 +27,6 @@ fillWidth: true - Keys.onPressed: event => { - event.accepted = pinField.handleKeyEvent(event.key, event.modifiers); - } onVisibleChanged: if (!visible) pinField.number = "" @@ -74,6 +73,12 @@ pinField.number = ""; } } + PasswordAnimation { + readonly property real availableHeight: root.height - grid.implicitHeight - root.spacing - root.margins * 2 - Style.dimens.pane_spacing + + Layout.bottomMargin: Style.dimens.pane_spacing + visible: !grid.isLandscape && availableHeight >= implicitHeight + } GridLayout { id: grid @@ -99,41 +104,13 @@ Layout.maximumWidth: Style.dimens.max_text_width spacing: 0 - AnimationLoader { - Layout.alignment: Qt.AlignHCenter + PasswordAnimation { Layout.bottomMargin: Style.dimens.pane_spacing - type: { - if (grid.isLandscape && !Style.is_layout_desktop) { - return AnimationLoader.NONE; - } - switch (root.passwordType) { - case NumberModel.PasswordType.TRANSPORT_PIN: - return AnimationLoader.TRANSPORT_PIN; - case NumberModel.PasswordType.CAN: - return AnimationLoader.CAN; - case NumberModel.PasswordType.SMART_PIN: - case NumberModel.PasswordType.PIN: - return AnimationLoader.PIN; - case NumberModel.PasswordType.NEW_PIN_CONFIRMATION: - case NumberModel.PasswordType.NEW_PIN: - case NumberModel.PasswordType.NEW_SMART_PIN: - case NumberModel.PasswordType.NEW_SMART_PIN_CONFIRMATION: - return AnimationLoader.NEW_PIN; - case NumberModel.PasswordType.PUK: - return AnimationLoader.PUK; - case NumberModel.PasswordType.REMOTE_PIN: - return AnimationLoader.REMOTE_PIN; - default: - return AnimationLoader.NONE; - } - } + visible: grid.isLandscape && Style.is_layout_desktop } - GText { + Heading { id: mainText - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - //: LABEL ALL_PLATFORMS This is the large main text below the icon. text: root.passwordType === NumberModel.PasswordType.CAN ? qsTr("Enter CAN") : //: LABEL ALL_PLATFORMS This is the large main text below the icon. @@ -154,7 +131,6 @@ root.passwordType === NumberModel.PasswordType.NEW_SMART_PIN_CONFIRMATION ? qsTr("Confirm Smart-eID PIN") : //: LABEL ALL_PLATFORMS This is the large main text below the icon. qsTr("Enter ID card PIN") - textStyle: Style.text.headline } GText { id: infoText @@ -264,7 +240,7 @@ background: Rectangle { border.color: Style.color.border - border.width: Style.dimens.separator_size + border.width: Style.dimens.border_width color: Style.color.transparent radius: Style.dimens.control_radius } @@ -329,7 +305,7 @@ onDeletePressed: { pinField.removeLast(); if (pinField.number.length === 0) - pinField.forceActiveFocus(); + numberPad.forceActiveFocus(); } onDigitPressed: digit => pinField.append(digit) onSubmitPressed: d.setPassword() @@ -345,4 +321,30 @@ style: ConfirmationPopup.PopupStyle.OkButton text: infoText.text } + + component PasswordAnimation: AnimationLoader { + Layout.alignment: Qt.AlignHCenter + type: { + switch (root.passwordType) { + case NumberModel.PasswordType.TRANSPORT_PIN: + return AnimationLoader.Type.TRANSPORT_PIN; + case NumberModel.PasswordType.CAN: + return AnimationLoader.Type.CAN; + case NumberModel.PasswordType.SMART_PIN: + case NumberModel.PasswordType.PIN: + return AnimationLoader.Type.PIN; + case NumberModel.PasswordType.NEW_PIN_CONFIRMATION: + case NumberModel.PasswordType.NEW_PIN: + case NumberModel.PasswordType.NEW_SMART_PIN: + case NumberModel.PasswordType.NEW_SMART_PIN_CONFIRMATION: + return AnimationLoader.Type.NEW_PIN; + case NumberModel.PasswordType.PUK: + return AnimationLoader.Type.PUK; + case NumberModel.PasswordType.REMOTE_PIN: + return AnimationLoader.Type.REMOTE_PIN; + default: + return AnimationLoader.Type.NONE; + } + } + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/EnterPasswordView/internal/NumberPad.qml ausweisapp2-2.4.0/src/ui/qml/modules/EnterPasswordView/internal/NumberPad.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/EnterPasswordView/internal/NumberPad.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/EnterPasswordView/internal/NumberPad.qml 2025-10-30 10:10:48.000000000 +0000 @@ -8,8 +8,9 @@ import QtQuick.Layouts import Governikus.Global import Governikus.Type +import Governikus.View -GridLayout { +Item { id: root property bool deleteEnabled: true @@ -20,75 +21,110 @@ signal digitPressed(string digit) signal submitPressed - //: LABEL ANDROID IOS - Accessible.description: qsTr("Number pad") - Accessible.focusable: true - Accessible.role: Accessible.Grouping - columnSpacing: 10 - columns: 3 - rowSpacing: columnSpacing - - QtObject { - id: d - - readonly property var numbers: { - let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; - if (root.visible && SettingsModel.shuffleScreenKeyboard) { - Utils.shuffle(numbers); + Layout.fillHeight: true + Layout.fillWidth: true + Layout.maximumHeight: buttonLayout.Layout.maximumWidth + Layout.maximumWidth: buttonLayout.Layout.maximumWidth + Layout.minimumHeight: buttonLayout.Layout.minimumHeight + Layout.minimumWidth: buttonLayout.Layout.minimumWidth + implicitHeight: buttonLayout.implicitHeight + implicitWidth: buttonLayout.implicitWidth + + GridLayout { + id: buttonLayout + + Accessible.focusable: true + //: LABEL ANDROID IOS + Accessible.name: qsTr("Number pad") + Accessible.role: Accessible.Grouping + activeFocusOnTab: true + anchors.fill: parent + columnSpacing: 10 + columns: 3 + rowSpacing: columnSpacing + + Keys.onPressed: event => { + if (!buttonLayout.activeFocus) { + event.accepted = false; + } else if (event.key >= Qt.Key_0 && event.key <= Qt.Key_9) { + root.digitPressed((event.key - Qt.Key_0).toString()); + event.accepted = true; + } else if (event.key === Qt.Key_Backspace || event.key === Qt.Key_Delete) { + root.deletePressed(); + event.accepted = true; + } else if ((event.key === Qt.Key_Enter || event.key === Qt.Key_Return) && root.submitEnabled) { + root.submitPressed(); + event.accepted = true; + } else { + event.accepted = false; } - return numbers; } - } - Repeater { - id: numberRepeater - model: 9 + QtObject { + id: d + + readonly property var numbers: { + let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + if (root.visible && SettingsModel.shuffleScreenKeyboard) { + Utils.shuffle(numbers); + } + return numbers; + } + } + Repeater { + id: numberRepeater + + model: 9 + + NumberPadButton { + required property int index + Layout.fillHeight: true + Layout.fillWidth: true + text: d.numbers[index] + + onClicked: root.digitPressed(text) + } + } NumberPadButton { - required property int index + Layout.fillHeight: true + Layout.fillWidth: true + + //: LABEL ANDROID IOS A11y text for the "delete" button text when the button is disabled. + a11yDisabledText: qsTr("Delete last digit, disabled until input is present.") + //: LABEL ANDROID IOS A11y text for the "delete" button image. + a11yText: qsTr("Delete last digit") + enabled: root.deleteEnabled + icon.source: "qrc:///images/material_backspace.svg" + onClicked: root.deletePressed() + } + NumberPadButton { + Layout.column: 1 Layout.fillHeight: true Layout.fillWidth: true - text: d.numbers[index] + Layout.row: 3 + text: d.numbers[9] onClicked: root.digitPressed(text) } - } - NumberPadButton { - Layout.fillHeight: true - Layout.fillWidth: true - - //: LABEL ANDROID IOS A11y text for the "delete" button text when the button is disabled. - a11yDisabledText: qsTr("Delete last digit, disabled until input is present.") - //: LABEL ANDROID IOS A11y text for the "delete" button image. - a11yText: qsTr("Delete last digit") - enabled: root.deleteEnabled - icon.source: "qrc:///images/material_backspace.svg" + SubmitButton { + id: submitButton - onClicked: root.deletePressed() - } - NumberPadButton { - Layout.column: 1 - Layout.fillHeight: true - Layout.fillWidth: true - Layout.row: 3 - text: d.numbers[9] - - onClicked: root.digitPressed(text) - } - SubmitButton { - id: submitButton - - Layout.fillHeight: true - Layout.fillWidth: true + Layout.fillHeight: true + Layout.fillWidth: true - //: LABEL ANDROID IOS A11y text, appended onto the "submit" button text when the button is disabled. - a11yDisabledText: a11yText + qsTr(", disabled until input is complete.") - //: LABEL ANDROID IOS A11y text for the "submit" button image. - a11yText: qsTr("Submit") - enabled: root.submitEnabled - icon.source: "qrc:///images/material_check.svg" + //: LABEL ANDROID IOS A11y text, appended onto the "submit" button text when the button is disabled. + a11yDisabledText: a11yText + qsTr(", disabled until input is complete.") + //: LABEL ANDROID IOS A11y text for the "submit" button image. + a11yText: qsTr("Submit") + enabled: root.submitEnabled + icon.source: "qrc:///images/material_check.svg" - onClicked: root.submitPressed() + onClicked: root.submitPressed() + } + } + FocusFrame { + scope: buttonLayout } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/EnterPasswordView/internal/NumberPadButton.qml ausweisapp2-2.4.0/src/ui/qml/modules/EnterPasswordView/internal/NumberPadButton.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/EnterPasswordView/internal/NumberPadButton.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/EnterPasswordView/internal/NumberPadButton.qml 2025-10-30 10:10:48.000000000 +0000 @@ -3,14 +3,13 @@ */ import QtQuick -import QtQuick.Controls import QtQuick.Layouts import Governikus.Global import Governikus.Style import Governikus.View import Governikus.Type -AbstractButton { +GAbstractButton { id: root property string a11yDisabledText: qsTr("Disabled") diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+desktop/LogView.qml ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+desktop/LogView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+desktop/LogView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+desktop/LogView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -17,28 +17,32 @@ SectionPage { id: root - signal keyPressed(int key) + signal keyPressed(var pEvent) //: LABEL DESKTOP title: qsTr("Logs") titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.leaveView() } Keys.onPressed: event => { - keyPressed(event.key); + keyPressed(event); } + LogModel { + id: logModel + + } TabbedPane { id: tabbedPane anchors.fill: parent contentDelegate: logContentDelegate contentRightMargin: 0 - sectionsModel: LogModel.logFileNames + sectionsModel: LogFilesModel footerItem: ColumnLayout { spacing: Style.dimens.groupbox_spacing @@ -47,14 +51,14 @@ id: saveLog Layout.maximumWidth: Number.POSITIVE_INFINITY - enabled: tabbedPane.sectionsModel.length > 0 + enabled: LogFilesModel.count > 0 icon.source: "qrc:///images/desktop/save_icon.svg" //: LABEL DESKTOP text: qsTr("Save log") tintIcon: true onClicked: { - let filenameSuggestion = LogModel.createLogFileName(LogModel.getCurrentLogFileDate()); + let filenameSuggestion = logModel.createLogFileName(); fileDialog.selectFile(filenameSuggestion); } @@ -68,13 +72,13 @@ //: LABEL DESKTOP title: qsTr("Save log") - onAccepted: LogModel.saveCurrentLogFile(file) + onAccepted: logModel.saveLogFile(selectedFile, true) } } GButton { Layout.maximumWidth: Number.POSITIVE_INFINITY disabledTooltipText: qsTr("The current log will be automatically deleted at exit.") - enableButton: tabbedPane.sectionsModel.length > 1 + enableButton: LogFilesModel.count > 1 icon.source: "qrc:///images/trash_icon.svg" //: LABEL DESKTOP text: qsTr("Delete all logs") @@ -94,7 +98,7 @@ } } - onCurrentIndexChanged: LogModel.setLogFile(currentIndex) + onCurrentIndexChanged: logModel.source = LogFilesModel.getLogFilePath(currentIndex) } Component { id: logContentDelegate @@ -102,11 +106,10 @@ GListView { id: logView - activeFocusOnTab: true displayMarginBeginning: Style.dimens.pane_padding displayMarginEnd: Style.dimens.pane_padding implicitHeight: tabbedPane.availableHeight - model: LogModel + model: logModel delegate: FocusScope { id: logEntry @@ -121,6 +124,10 @@ implicitHeight: logDelegate.implicitHeight + logDelegate.anchors.topMargin + logDelegate.anchors.bottomMargin width: logView.width - Style.dimens.pane_padding + onActiveFocusChanged: if (activeFocus) { + logView.handleItemFocused(index); + } + RoundedRectangle { anchors.fill: parent bottomLeftCorner: logEntry.isLastItem @@ -159,11 +166,11 @@ logView.positionViewAtEnd(); } - target: LogModel + target: logModel } Connections { - function onKeyPressed(pKey) { - logView.handleKeyPress(pKey); + function onKeyPressed(pEvent) { + logView.handleKeyPress(pEvent); } target: root @@ -179,8 +186,8 @@ text: qsTr("All old logs will be deleted.") //: LABEL DESKTOP title: qsTr("Delete all logs") - width: UiPluginModel.scaleFactor * 600 + width: UiPluginModel.scaleFactor * 360 - onConfirmed: LogModel.removeOtherLogFiles() + onConfirmed: LogFilesModel.removeOtherLogFiles() } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+desktop/internal/DetachedLogView.qml ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+desktop/internal/DetachedLogView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+desktop/internal/DetachedLogView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+desktop/internal/DetachedLogView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -23,12 +23,17 @@ color: Style.color.background Keys.onPressed: event => { - listView.handleKeyPress(event.key); + listView.handleKeyPress(event); } + LogModel { + id: logModel + + } LogFilterModel { id: filterModel + sourceModel: logModel } LogTextStyle { id: logTextStyle @@ -43,30 +48,15 @@ Layout.margins: root.spacing spacing: root.spacing - ColumnLayout { - GText { - //: LABEL DESKTOP - text: qsTr("Select log:") - textStyle: logTextStyle - } - GComboBox { - Layout.preferredWidth: 200 - horizontalPadding: root.horizontalPadding - model: LogModel.logFileNames - radius: root.controlRadius - textStyle: logTextStyle - verticalPadding: root.verticalPadding - - background: GPaneBackground { - border.color: logTextStyle.textColor - border.width: 1 - color: Style.color.transparent - drawShadow: false - radius: root.controlRadius - } - - onCurrentIndexChanged: LogModel.setLogFile(currentIndex) - } + GText { + Accessible.role: Accessible.Heading + Layout.alignment: Qt.AlignBottom + //: LABEL DESKTOP + text: qsTr("Current Log") + textStyle: Style.text.title + } + GSpacer { + Layout.fillWidth: true } ColumnLayout { GText { @@ -102,16 +92,13 @@ onClicked: filter = !filter } - Item { - Layout.fillWidth: true - } LogButton { icon.source: "qrc:///images/desktop/save_icon.svg" //: LABEL DESKTOP text: qsTr("Save log") onClicked: { - let filenameSuggestion = LogModel.createLogFileName(LogModel.getCurrentLogFileDate()); + let filenameSuggestion = logModel.createLogFileName(); fileDialog.selectFile(filenameSuggestion); } @@ -125,7 +112,7 @@ //: LABEL DESKTOP title: qsTr("Save log") - onAccepted: LogModel.saveCurrentLogFile(file) + onAccepted: logModel.saveLogFile(selectedFile, true) } } } @@ -225,20 +212,18 @@ Layout.fillWidth: true Layout.leftMargin: root.spacing Layout.topMargin: root.spacing - activeFocusOnTab: true clip: true model: filterModel - ScrollBar.vertical: GScrollBar { - borderWidth: 1 - bottomPadding: 0 - implicitWidth: 6 + leftPadding + rightPadding - rightPadding: 3 - topPadding: 0 - } delegate: LogViewDelegate { + required property int index + font.pixelSize: 0.12 * zoomBox.value width: listView.width - root.spacing + + onActiveFocusChanged: if (activeFocus) { + listView.handleItemFocused(index); + } } Connections { @@ -247,7 +232,7 @@ listView.positionViewAtEnd(); } - target: LogModel + target: logModel } GText { horizontalAlignment: Text.AlignHCenter diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+desktop/internal/LogViewDelegate.qml ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+desktop/internal/LogViewDelegate.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+desktop/internal/LogViewDelegate.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+desktop/internal/LogViewDelegate.qml 2025-10-30 10:10:48.000000000 +0000 @@ -21,10 +21,11 @@ ApplicationModel.showFeedback(qsTr("The log entry was copied to the clipboard.")); } + Accessible.role: Utils.useSpecialAppleTabRole(Accessible.StaticText) color: level === "C" ? Style.color.warning : (level === "W" ? Style.color.textSubline.basic : textStyle.textColor) focusFrameVisible: false - font.bold: activeFocus font.family: UiPluginModel.fixedFontFamily + font.weight: activeFocus ? Style.font.bold : Style.font.normal lineHeight: 1.0 lineHeightMode: Text.ProportionalHeight text: "%1 %2".arg(origin).arg(message) diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/+ios/StoreFeedbackPopup.qml ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/+ios/StoreFeedbackPopup.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/+ios/StoreFeedbackPopup.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/+ios/StoreFeedbackPopup.qml 2025-10-30 10:10:48.000000000 +0000 @@ -5,6 +5,8 @@ import Governikus.Type Item { + function close() { + } function open() { ApplicationModel.showAppStoreRatingDialog(); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/ListItem.qml ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/ListItem.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/ListItem.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/ListItem.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,117 +0,0 @@ -/** - * Copyright (c) 2015-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick -import QtQuick.Layouts - -import Governikus.Global -import Governikus.Type -import Governikus.Style - -Rectangle { - id: root - - property bool boldFont: false - property real contentMarginLeft: Style.dimens.groupbox_spacing - property real contentMarginRight: Style.dimens.groupbox_spacing - property alias footerText: footerItem.text - property alias headerText: headerItem.text - property alias icon: imageItem.source - property alias mouseAreaEnabled: mouseArea.enabled - property bool showSeparator: true - property alias text: textItem.text - property alias tintIcon: imageItem.tintEnabled - - signal pressAndHold - - Accessible.name: headerText + ". " + text + ". " + footerText - Accessible.role: Accessible.ListItem - color: colors.paneBackground - height: content.implicitHeight + Style.dimens.text_spacing - width: parent ? parent.width : 0 - - GSeparator { - anchors.right: parent.right - anchors.top: parent.bottom - anchors.topMargin: -height - visible: root.showSeparator - width: Style.is_layout_ios ? (parent.width - textLayout.x - root.contentMarginLeft) : parent.width - } - RowLayout { - id: content - - anchors.fill: parent - anchors.leftMargin: root.contentMarginLeft - anchors.rightMargin: root.contentMarginRight - spacing: Style.dimens.groupbox_spacing - - TintableIcon { - id: imageItem - - sourceSize.height: parent.height - 2 * Style.dimens.groupbox_spacing - tintColor: colors.textNormal - tintEnabled: false - visible: root.icon !== "" - } - ColumnLayout { - id: textLayout - - Layout.fillWidth: true - Layout.maximumWidth: Number.POSITIVE_INFINITY - spacing: 0 - - GText { - id: headerItem - - Accessible.ignored: true - Layout.alignment: Qt.AlignLeft - activeFocusOnTab: false - color: colors.textSubline - elide: Text.ElideRight - font.bold: root.boldFont - maximumLineCount: 8 - visible: root.headerText !== "" - } - GText { - id: textItem - - Accessible.ignored: true - Layout.alignment: Qt.AlignLeft - activeFocusOnTab: false - color: colors.textNormal - elide: Text.ElideRight - font.bold: root.boldFont - maximumLineCount: 64 - visible: root.text !== "" - } - GText { - id: footerItem - - Accessible.ignored: true - Layout.alignment: Qt.AlignLeft - activeFocusOnTab: false - color: colors.textNormal - elide: Text.ElideRight - font.bold: root.boldFont - maximumLineCount: 8 - visible: root.footerText !== "" - } - } - } - MouseArea { - id: mouseArea - - anchors.fill: parent - hoverEnabled: UiPluginModel.isChromeOS - - onPressAndHold: root.pressAndHold() - } - StatefulColors { - id: colors - - checkedCondition: false - hoveredCondition: mouseArea.containsMouse - statefulControl: mouseArea - } -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/LogFilesView.qml ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/LogFilesView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/LogFilesView.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/LogFilesView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts + +import Governikus.Global +import Governikus.Style +import Governikus.TitleBar +import Governikus.View +import Governikus.Type + +FlickableSectionPage { + id: root + + signal logFilesListItemClicked(int index) + + //: LABEL ANDROID IOS + title: qsTr("Select Log") + + navigationAction: NavigationAction { + action: NavigationAction.Action.Back + + onClicked: root.pop() + } + rightTitleBarAction: LogTitleBarControls { + showRemove: LogFilesModel.count > 1 + + onRemoveAllClicked: confirmationPopup.open() + } + + GOptionsContainer { + Layout.fillWidth: true + + Repeater { + id: repeater + + model: LogFilesModel + + delegate: ColumnLayout { + required property int index + required property string modelData + + spacing: 0 + + GMenuItem { + Layout.fillWidth: true + drawBottomCorners: parent.index == repeater.count - 1 + drawTopCorners: parent.index == 0 + title: parent.modelData + + onClicked: root.logFilesListItemClicked(parent.index) + } + GSeparator { + Layout.fillWidth: true + Layout.leftMargin: Style.dimens.pane_spacing + Layout.rightMargin: Layout.leftMargin + visible: parent.index != repeater.count - 1 + } + } + } + } + ConfirmationPopup { + id: confirmationPopup + + //: LABEL ANDROID IOS + okButtonText: qsTr("Delete") + //: INFO ANDROID IOS All logfiles are about to be removed, user confirmation required. + text: qsTr("All old logs will be deleted.") + //: LABEL ANDROID IOS + title: qsTr("Delete all logs") + + onConfirmed: LogFilesModel.removeOtherLogFiles() + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/LogView.qml ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/LogView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/LogView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/LogView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -16,8 +16,11 @@ SectionPage { id: root - //: LABEL ANDROID IOS - title: qsTr("Log") + property string logFileName: "not set" + property alias logFilePath: logModel.source + + contentIsScrolled: !logView.atYBeginning + title: root.logFileName navigationAction: NavigationAction { action: NavigationAction.Action.Back @@ -25,79 +28,33 @@ onClicked: root.pop() } rightTitleBarAction: LogTitleBarControls { - allowRemoveAll: comboBox.model.length > 1 // comboBox.count doesn't seem to update reliably + showFilter: true + showShare: true - onRemoveAll: { - confirmationPopup.open(); - } - onShare: pPopupPosition => { - LogModel.shareLog(pPopupPosition); + onFilterChanged: logStack.currentIndex = filter ? 0 : 1 + onShareClicked: pPopupPosition => { + logModel.shareLogFile(pPopupPosition); } } - Connections { - function onActivate() { - logView.highlightScrollbar(); - } + LogModel { + id: logModel + } LogFilterModel { id: filterModel + sourceModel: logModel } - ColumnLayout { - anchors.fill: parent - spacing: 0 - - Rectangle { - id: logSelector - - Layout.fillWidth: true - Layout.preferredHeight: comboBox.height + Style.dimens.pane_padding * 2 - color: Style.color.paneSublevel.background.basic - - GComboBox { - id: comboBox - - Accessible.description: qsTr("Select log from list.") - model: LogModel.logFileNames - - onCurrentIndexChanged: LogModel.setLogFile(currentIndex) - - anchors { - left: parent.left - leftMargin: Style.dimens.pane_padding - right: filterButton.left - rightMargin: Style.dimens.pane_spacing - top: parent.top - topMargin: Style.dimens.pane_padding - } - } - TitleBarAction { - id: filterButton + StackLayout { + id: logStack - property bool filter: false + anchors.fill: parent + currentIndex: 1 - Accessible.checked: filter - Accessible.name: qsTr("Filter") - Accessible.role: Accessible.CheckBox - icon.source: filter ? "qrc:///images/filter_off.svg" : "qrc:///images/filter.svg" - iconTintColor: comboBox.textStyle.textColor - - onClicked: filter = !filter - - anchors { - right: parent.right - rightMargin: Style.dimens.pane_padding - verticalCenter: comboBox.verticalCenter - } - } - } GFlickableColumnLayout { - Layout.fillHeight: true - Layout.fillWidth: true clip: true spacing: Style.dimens.text_spacing - visible: filterButton.filter GOptionsContainer { Layout.fillWidth: true @@ -106,16 +63,15 @@ //: LABEL ANDROID IOS title: qsTr("Filter") - GText { + Subheading { //: LABEL ANDROID IOS text: qsTr("Level") - textStyle: Style.text.subline } - Grid { + GridLayout { columnSpacing: Style.dimens.groupbox_spacing - columns: (width + columnSpacing) / (levelRepeater.maxItemWidth + columnSpacing) + columns: Math.max(1, (parent.width + columnSpacing) / (levelRepeater.maxItemWidth + columnSpacing)) rowSpacing: Style.dimens.groupbox_spacing - width: parent.width + uniformCellWidths: true GRepeater { id: levelRepeater @@ -127,22 +83,20 @@ checked: filterModel.selectedLevels.indexOf(text) !== -1 text: modelData - width: levelRepeater.maxItemWidth onCheckedChanged: filterModel.configureLevel(text, checked) } } } - GText { + Subheading { //: LABEL ANDROID IOS text: qsTr("Category") - textStyle: Style.text.subline } - Grid { + GridLayout { columnSpacing: Style.dimens.groupbox_spacing - columns: (width + columnSpacing) / (categoryRepeater.maxItemWidth + columnSpacing) + columns: Math.max(1, (parent.width + columnSpacing) / (categoryRepeater.maxItemWidth + columnSpacing)) rowSpacing: Style.dimens.groupbox_spacing - width: parent.width + uniformCellWidths: true GRepeater { id: categoryRepeater @@ -154,7 +108,6 @@ checked: filterModel.selectedCategories.indexOf(text) !== -1 text: modelData - width: categoryRepeater.maxItemWidth onCheckedChanged: filterModel.configureCategory(text, checked) } @@ -165,33 +118,17 @@ GListView { id: logView - Accessible.ignored: false Layout.fillHeight: true Layout.fillWidth: true - activeFocusOnTab: true clip: true model: filterModel - visible: !filterButton.filter - delegate: ListItem { - required property int index - readonly property bool isLastItem: index === ListView.view.count - 1 - required property string message - required property string modelData - required property string origin - - boldFont: ListView.isCurrentItem && logView.focus - headerText: origin - showSeparator: !isLastItem - text: message - - Accessible.onScrollDownAction: (ListView.view as GListView).scrollPageDown() - Accessible.onScrollUpAction: (ListView.view as GListView).scrollPageUp() - onPressAndHold: { - ApplicationModel.setClipboardText(modelData); - //: INFO ANDROID IOS Toast message used to confirm the copy of a log entry. - ApplicationModel.showFeedback(qsTr("The log entry was copied to the clipboard.")); - } + delegate: LogViewDelegate { + boldFont: ListView.isCurrentItem && logView.activeFocus + width: logView.width + + Accessible.onScrollDownAction: logView.scrollPageDown() + Accessible.onScrollUpAction: logView.scrollPageUp() } Connections { @@ -200,7 +137,7 @@ logView.positionViewAtEnd(); } - target: LogModel + target: logModel } GText { anchors.centerIn: parent @@ -212,16 +149,4 @@ } } } - ConfirmationPopup { - id: confirmationPopup - - //: LABEL ANDROID IOS - okButtonText: qsTr("Delete") - //: INFO ANDROID IOS All logfiles are about to be removed, user confirmation required. - text: qsTr("All old logs will be deleted.") - //: LABEL ANDROID IOS - title: qsTr("Delete all logs") - - onConfirmed: LogModel.removeOtherLogFiles() - } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/internal/LogTitleBarControls.qml ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/internal/LogTitleBarControls.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/internal/LogTitleBarControls.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/internal/LogTitleBarControls.qml 2025-10-30 10:10:48.000000000 +0000 @@ -10,25 +10,48 @@ Row { id: root - property alias allowRemoveAll: removeAllButton.visible + property alias filter: filterButton.filter + property alias showFilter: filterButton.visible + property alias showRemove: removeAllButton.visible + property alias showShare: shareButton.visible - signal removeAll - signal share(point popupPosition) + signal removeAllClicked + signal shareClicked(point popupPosition) spacing: Style.dimens.pane_spacing TitleBarAction { + id: filterButton + + property bool filter: false + + Accessible.checked: filter + //: LABEL ANDROID IOS + Accessible.name: qsTr("Filter") + Accessible.role: Accessible.CheckBox + icon.source: filter ? "qrc:///images/filter_off.svg" : "qrc:///images/filter.svg" + visible: false + + onClicked: filter = !filter + } + TitleBarAction { + id: shareButton + + //: LABEL ANDROID IOS Accessible.name: qsTr("Share log") icon.source: "qrc:///images/mobile/share.svg" + visible: false - onClicked: root.share(mapToGlobal(width / 2, height)) + onClicked: root.shareClicked(mapToGlobal(width / 2, height)) } TitleBarAction { id: removeAllButton + //: LABEL ANDROID IOS Accessible.name: qsTr("Delete all logs") icon.source: "qrc:///images/trash_icon.svg" + visible: false - onClicked: root.removeAll() + onClicked: root.removeAllClicked() } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/internal/LogViewDelegate.qml ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/internal/LogViewDelegate.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/FeedbackView/+mobile/internal/LogViewDelegate.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/FeedbackView/+mobile/internal/LogViewDelegate.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2015-2025 Governikus GmbH & Co. KG, Germany + */ + +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts + +import Governikus.Global +import Governikus.Type +import Governikus.Style + +Rectangle { + id: root + + property bool boldFont: false + required property int index + required property string message + required property string modelData + required property string origin + + Accessible.focusable: true + Accessible.name: modelData + Accessible.role: Utils.useSpecialAppleTabRole(Accessible.ListItem) + color: colors.paneBackground + implicitHeight: content.implicitHeight + content.anchors.topMargin + content.anchors.bottomMargin + implicitWidth: content.implicitWidth + + GSeparator { + visible: root.index !== 0 + + anchors { + left: parent.left + right: parent.right + top: parent.top + } + } + ColumnLayout { + id: content + + spacing: 0 + + anchors { + bottomMargin: Style.dimens.text_spacing / 2 + fill: parent + leftMargin: Style.dimens.pane_padding + rightMargin: anchors.leftMargin + topMargin: anchors.bottomMargin + } + LogText { + color: Style.color.textSubline.basic + maximumLineCount: 8 + text: root.origin + } + LogText { + maximumLineCount: 64 + text: root.message + } + } + MouseArea { + id: mouseArea + + anchors.fill: parent + hoverEnabled: UiPluginModel.isChromeOS + + onPressAndHold: { + ApplicationModel.setClipboardText(root.modelData); + //: INFO ANDROID IOS Toast message used to confirm the copy of a log entry. + ApplicationModel.showFeedback(qsTr("The log entry was copied to the clipboard.")); + } + } + StatefulColors { + id: colors + + checkedCondition: false + hoveredCondition: mouseArea.containsMouse + statefulControl: mouseArea + } + + component LogText: GText { + Accessible.ignored: true + font.family: UiPluginModel.fixedFontFamily + font.weight: root.boldFont ? Style.font.bold : Style.font.normal + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+6.5/GDropShadow.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+6.5/GDropShadow.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+6.5/GDropShadow.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+6.5/GDropShadow.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/** - * Copyright (c) 2023-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick - -ShaderEffect { - property bool autoPaddingEnabled - property rect paddingRect - property real shadowOpacity - property real shadowScale - property real shadowVerticalOffset -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+6.7/GColorOverlay.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+6.7/GColorOverlay.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+6.7/GColorOverlay.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+6.7/GColorOverlay.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/** - * Copyright (c) 2024-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick - -ShaderEffect { - readonly property color color: colorizationColor - property color colorizationColor - - fragmentShader: "qrc:/shader/ColorOverlayShader.frag" -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+6.7/GDesaturate.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+6.7/GDesaturate.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+6.7/GDesaturate.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+6.7/GDesaturate.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -/** - * Copyright (c) 2024-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick - -ShaderEffect { - fragmentShader: "qrc:/shader/DesaturateShader.frag" -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/CardNotActivatedView.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/CardNotActivatedView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/CardNotActivatedView.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/CardNotActivatedView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +pragma ComponentBehavior: Bound + +import QtQuick + +import Governikus.Global +import Governikus.TitleBar + +CardNotActivatedBaseView { + id: root + + onDecisionHasCodeClicked: root.pushSubView(true, { + titleBarSettings: titleBarSettings.createObject(root) + }) + onDecisionHasNoCodeClicked: root.pushSubView(false, { + titleBarSettings: titleBarSettings.createObject(root) + }) + + Component { + id: titleBarSettings + + TitleBarSettings { + navigationAction: NavigationAction.Action.Back + + onNavigationActionClicked: root.pop() + } + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/ConfirmationPopup.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/ConfirmationPopup.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/ConfirmationPopup.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/ConfirmationPopup.qml 2025-10-30 10:10:48.000000000 +0000 @@ -2,7 +2,7 @@ * Copyright (c) 2018-2025 Governikus GmbH & Co. KG, Germany */ -import QtQuick +import QtQuick.Layouts import Governikus.Global import Governikus.Style @@ -10,22 +10,26 @@ BaseConfirmationPopup { id: root - buttons: Row { - layoutDirection: Qt.RightToLeft - spacing: Style.dimens.pane_spacing + buttons: RowLayout { + spacing: 0 width: parent.width - GButton { - text: root.okButtonText - visible: root.style & ConfirmationPopup.PopupStyle.OkButton - - onClicked: root.accept() + GSpacer { + Layout.fillWidth: true } GButton { + style: root.style & ConfirmationPopup.PopupStyle.OkButton ? Style.color.controlOptional : Style.color.control text: root.cancelButtonText visible: root.style & ConfirmationPopup.PopupStyle.CancelButton onClicked: root.cancel() } + GButton { + Layout.leftMargin: root.style & ConfirmationPopup.PopupStyle.CancelButton ? Style.dimens.pane_spacing : 0 + text: root.okButtonText + visible: root.style & ConfirmationPopup.PopupStyle.OkButton + + onClicked: root.accept() + } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/GFileDialog.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/GFileDialog.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/GFileDialog.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/GFileDialog.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,17 +1,21 @@ /** * Copyright (c) 2021-2025 Governikus GmbH & Co. KG, Germany */ + +import QtCore import QtQuick -import Qt.labs.platform as Labs +import QtQuick.Dialogs -Labs.FileDialog { +FileDialog { function selectFile(pDefaultFilename) { - currentFile = folder + "/" + pDefaultFilename; + let isFileUrl = /^file:\/\//; + let folder = isFileUrl.test(currentFolder) ? currentFolder : "file://" + currentFolder; + selectedFile = folder + "/" + pDefaultFilename; open(); } - fileMode: Labs.FileDialog.SaveFile - folder: Labs.StandardPaths.writableLocation(Labs.StandardPaths.DocumentsLocation) + currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation) + fileMode: FileDialog.SaveFile Component.onDestruction: reject() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/GMenuItem.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/GMenuItem.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/GMenuItem.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/GMenuItem.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,6 +14,7 @@ property alias buttonTooltip: button.enabledTooltipText property string description: "" property alias iconSource: icon.source + property string linkToOpen property alias tintIcon: icon.tintEnabled required property string title @@ -40,8 +41,19 @@ GButton { id: button + readonly property bool hasLink: root.linkToOpen !== "" + + Accessible.description: hasLink ? Utils.platformAgnosticLinkOpenText(root.linkToOpen, Accessible.name) : "" + Accessible.name: (root.title && hasLink ? root.title + ", " : "") + text + Accessible.role: hasLink ? Accessible.Link : Accessible.Button tintIcon: true - onClicked: root.clicked() + onClicked: { + if (hasLink) { + Qt.openUrlExternally(root.linkToOpen); + } else { + root.clicked(); + } + } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/GPane.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/GPane.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/GPane.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/GPane.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2016-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick -import QtQuick.Layouts - -import Governikus.Global -import Governikus.Style - -GPaneBackground { - id: root - - property alias content: paneContent - property int contentPadding: Style.dimens.pane_padding - default property alias data: paneContent.data - property alias spacing: paneContent.spacing - property alias title: titleText.text - property int titleMargins: Style.dimens.pane_padding - property alias titleTextStyle: titleText.textStyle - - Layout.maximumHeight: containerCol.Layout.maximumHeight - implicitHeight: containerCol.implicitHeight - implicitWidth: containerCol.implicitWidth - - ColumnLayout { - id: containerCol - - anchors.fill: parent - spacing: 0 - - GText { - id: titleText - - Layout.leftMargin: root.titleMargins - Layout.rightMargin: root.titleMargins - Layout.topMargin: root.titleMargins - elide: Text.ElideRight - maximumLineCount: 1 - textStyle: Style.text.subline - visible: text !== "" - } - ColumnLayout { - id: paneContent - - Layout.bottomMargin: root.contentPadding - Layout.leftMargin: root.contentPadding - Layout.maximumWidth: Number.POSITIVE_INFINITY - Layout.rightMargin: root.contentPadding - Layout.topMargin: titleText.visible ? Style.dimens.pane_spacing : root.contentPadding - spacing: Style.dimens.pane_spacing - } - } -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/Hint.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/Hint.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/Hint.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/Hint.qml 2025-10-30 10:10:48.000000000 +0000 @@ -12,13 +12,16 @@ property alias buttonIconSource: hintButton.icon.source property alias buttonText: hintButton.text property alias buttonTooltip: hintButton.enabledTooltipText + property string linkToOpen property alias text: hintText.text signal clicked + signal linkAboutToOpen + Accessible.ignored: true color: Style.color.paneSublevel.background.basic + contentSpacing: 0 drawShadow: false - spacing: 0 GText { id: hintText @@ -29,12 +32,23 @@ GButton { id: hintButton + readonly property bool hasLink: root.linkToOpen !== "" + + Accessible.description: hasLink ? Utils.platformAgnosticLinkOpenText(root.linkToOpen, Accessible.name) : "" + Accessible.role: hasLink ? Accessible.Link : Accessible.Button Layout.alignment: Qt.AlignHCenter Layout.topMargin: Style.dimens.pane_spacing icon.source: "qrc:///images/open_website.svg" - tintIcon: hintText.color + tintIcon: true visible: text !== "" - onClicked: root.clicked() + onClicked: { + if (hasLink) { + root.linkAboutToOpen(); + Qt.openUrlExternally(root.linkToOpen); + } else { + root.clicked(); + } + } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/LocationButton.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/LocationButton.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/LocationButton.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/LocationButton.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Governikus.Global -import Governikus.Style -import Governikus.View -import Governikus.Type - -AbstractButton { - id: root - - required property string a11yDescription - required property string a11yName - required property url image - required property string language - required property string languageText - - Accessible.checkable: true - Accessible.checked: checked - Accessible.description: a11yDescription - Accessible.name: a11yName - Layout.maximumWidth: Number.POSITIVE_INFINITY - checked: SettingsModel.language === language - padding: Style.dimens.groupbox_spacing - - background: GPaneBackground { - border.color: colors.paneBorder - color: colors.paneBackground - drawShadow: false - - FocusFrame { - marginFactor: 0.8 - radius: parent.radius * 1.2 - scope: root - } - } - contentItem: ColumnLayout { - spacing: Style.dimens.pane_spacing - - Image { - Layout.alignment: Qt.AlignHCenter - source: root.image - sourceSize.height: Style.dimens.icon_size - } - GText { - Layout.alignment: Qt.AlignHCenter - activeFocusOnTab: false - color: colors.textNormal - text: root.languageText - } - } - - onClicked: SettingsModel.language = language - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) - - StatefulColors { - id: colors - - groupMember: true - statefulControl: root - } -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/ProxyCredentialsPopup.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/ProxyCredentialsPopup.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/ProxyCredentialsPopup.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/ProxyCredentialsPopup.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick + +import Governikus.Global +import Governikus.Type +import Governikus.Style + +ConfirmationPopup { + id: root + + property ProxyCredentials credentials + + //: LABEL DESKTOP Text of the button in the proxy credentials popup. + okButtonText: qsTr("Sign in") + + //: LABEL DESKTOP Title of the proxy credentials popup. + title: qsTr("Sign in") + width: formGrid.width + 2 * Style.dimens.pane_padding + + onClosed: { + if (!credentials) + return; + credentials.confirmInput(); + } + onConfirmed: { + if (!credentials) + return; + credentials.user = userInput.text; + credentials.password = passwordInput.text; + } + onCredentialsChanged: { + //: LABEL DESKTOP Text of the proxy credentials popup. An example for %1 is http://proxy.example.com:1337. + text = qsTr("The proxy %1 requires username and password.").arg(credentials ? credentials.url : ""); + userInput.text = credentials ? credentials.proposedUser : ""; + passwordInput.text = ""; + } + + Grid { + id: formGrid + + columns: 2 + spacing: Style.dimens.pane_spacing + verticalItemAlignment: Text.AlignVCenter + + GText { + //: LABEL DESKTOP Accessible name. + Accessible.name: qsTr("Proxy credential username") + + //: LABEL DESKTOP Label of the textfield for the username. + text: qsTr("Username") + } + GTextField { + id: userInput + + width: 300 * UiPluginModel.scaleFactor + } + GText { + //: LABEL DESKTOP Accessible name. + Accessible.name: qsTr("Proxy credential password") + + //: LABEL DESKTOP Label of the textfield for the password. + text: qsTr("Password") + } + GTextField { + id: passwordInput + + echoMode: TextInput.Password + width: 300 * UiPluginModel.scaleFactor + } + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/ReaderDetection.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/ReaderDetection.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/ReaderDetection.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/ReaderDetection.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick + +import Governikus.Type + +Item { + id: root + + signal newPcscReaderDetected + signal newRemoteReaderDetected + + Component.onCompleted: d.updateLastReaderCount() + + QtObject { + id: d + + property int lastPcscCount: 0 + property int lastRemoteCount: 0 + + function updateLastReaderCount() { + d.lastRemoteCount = ApplicationModel.availableRemoteReader; + d.lastPcscCount = ApplicationModel.availablePcscReader; + } + } + Connections { + function onFireAvailableReaderChanged() { + if (root.visible && root.enabled) { + if (ApplicationModel.availableRemoteReader > d.lastRemoteCount) { + root.newRemoteReaderDetected(); + } + if (ApplicationModel.availablePcscReader > d.lastPcscCount) { + root.newPcscReaderDetected(); + } + } + d.updateLastReaderCount(); + } + + target: ApplicationModel + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/TabbedPane.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/TabbedPane.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/TabbedPane.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/TabbedPane.qml 2025-10-30 10:10:48.000000000 +0000 @@ -26,12 +26,12 @@ property alias sectionCount: sectionNameList.count property var sectionsModel: undefined - function handleKeyPress(key) { + function handleKeyPress(event) { if (currentContentItem instanceof GListView) { - currentContentItem.handleKeyPress(key); + currentContentItem.handleKeyPress(event); return; } - flickable.handleKeyPress(key); + flickable.handleKeyPress(event); } function scrollYPositionIntoView(pYposition) { let dy = pYposition - flickable.contentY - flickable.height; @@ -77,7 +77,9 @@ GListView { id: sectionNameList - activeFocusOnTab: true + //: LABEL DESKTOP + Accessible.name: qsTr("Sidebar") + Accessible.role: Accessible.PageTabList anchors.fill: parent boundsBehavior: Flickable.StopAtBounds clip: true @@ -104,6 +106,10 @@ GFlickableColumnLayout { id: flickable + Accessible.ignored: !ApplicationModel.screenReaderRunning && (Qt.platform.os === "osx" || Qt.platform.os === "ios") + //: LABEL DESKTOP %1 will be replaced with the title of the tab + Accessible.name: root.sectionsModel ? qsTr("Content of tab \"%1\"").arg(root.sectionsModel[sectionNameList.currentIndex]) : "" + Accessible.role: Accessible.Grouping bottomMargin: 0 leftMargin: 0 topMargin: 0 diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/TabbedPaneDelegate.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/TabbedPaneDelegate.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+desktop/TabbedPaneDelegate.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+desktop/TabbedPaneDelegate.qml 2025-10-30 10:10:48.000000000 +0000 @@ -23,19 +23,32 @@ required property string modelData readonly property bool nextItemIsHighlighted: index === ListView.view.currentIndex - 1 || index === root.highlightedIndex - 1 + function select() { + ListView.view.itemAtIndex(index).forceActiveFocus(Qt.MouseFocusReason); + ListView.view.currentIndex = index; + } + Accessible.description: Qt.platform.os === "windows" ? a11yPageIndicator : "" Accessible.focusable: true Accessible.name: { if (Qt.platform.os === "windows") { return sectionName.text; } + + let a11yRole = index === ListView.view.currentIndex ? + //: LABEL DESKTOP + qsTr("Tab selected") : //: LABEL DESKTOP - return sectionName.text + ", " + qsTr("Tab selected") + ", " + a11yPageIndicator; + qsTr("Tab"); + + return sectionName.text + ", " + a11yRole + ", " + a11yPageIndicator; } Accessible.role: Accessible.PageTab height: sectionName.height + 2 * Style.dimens.pane_padding width: ListView.view.width + Accessible.onPressAction: select() + StatefulColors { id: colors @@ -59,7 +72,7 @@ GText { id: sectionName - activeFocusOnTab: false + Accessible.ignored: true color: colors.textNormal elide: Text.ElideRight maximumLineCount: 2 @@ -134,10 +147,7 @@ anchors.fill: parent hoverEnabled: true - onClicked: { - root.ListView.view.itemAtIndex(root.index).forceActiveFocus(Qt.MouseFocusReason); - root.ListView.view.currentIndex = root.index; - } + onClicked: root.select() onContainsMouseChanged: updateHighlight() onPressedChanged: updateHighlight() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/CardNotActivatedView.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/CardNotActivatedView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/CardNotActivatedView.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/CardNotActivatedView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +pragma ComponentBehavior: Bound + +import QtQuick + +import Governikus.Global +import Governikus.TitleBar + +CardNotActivatedBaseView { + id: root + + onDecisionHasCodeClicked: root.pushSubView(true, { + navigationAction: navigationAction.createObject(root) + }) + onDecisionHasNoCodeClicked: root.pushSubView(false, { + navigationAction: navigationAction.createObject(root) + }) + + Component { + id: navigationAction + + NavigationAction { + action: NavigationAction.Action.Back + + onClicked: root.pop() + } + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/GCollapsibleSubButton.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/GCollapsibleSubButton.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/GCollapsibleSubButton.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/GCollapsibleSubButton.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -/** - * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts - -import Governikus.Global -import Governikus.Style -import Governikus.View - -AbstractButton { - id: root - - property bool drawBottomCorners: false - required property url image - property alias tintIcon: icon.tintEnabled - - horizontalPadding: Style.dimens.pane_spacing * 2 - verticalPadding: Style.dimens.pane_spacing / 2 - - background: RoundedRectangle { - bottomLeftCorner: root.drawBottomCorners - bottomRightCorner: root.drawBottomCorners - color: colors.paneBackground - topLeftCorner: false - topRightCorner: false - } - contentItem: RowLayout { - spacing: Style.dimens.pane_spacing - - TintableIcon { - id: icon - - source: root.image - sourceSize.height: Style.dimens.small_icon_size - tintColor: label.color - tintEnabled: false - } - GText { - id: label - - Accessible.ignored: true - Layout.maximumWidth: Number.POSITIVE_INFINITY - color: colors.textNormal - text: root.text - } - } - - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) - - StatefulColors { - id: colors - - paneStyle: Style.color.paneSublevel - statefulControl: root - } - FocusFrame { - marginFactor: -1 - } -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/GMenuItem.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/GMenuItem.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/GMenuItem.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/GMenuItem.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,24 +1,27 @@ /** * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany */ + import QtQuick -import QtQuick.Controls import QtQuick.Layouts import Governikus.Global import Governikus.Style import Governikus.View -AbstractButton { +GAbstractButton { id: root property alias description: descriptionText.text property bool drawBottomCorners: false property bool drawTopCorners: false + readonly property bool hasLink: linkToOpen !== "" + property string linkToOpen property alias tintIcon: iconItem.tintEnabled property alias title: titleText.text + Accessible.description: hasLink ? Utils.platformAgnosticLinkOpenText(linkToOpen, Accessible.name) : "" Accessible.name: title + ". " + description - Accessible.role: Accessible.Button + Accessible.role: hasLink ? Accessible.Link : Accessible.Button icon.source: "qrc:///images/material_arrow_right.svg" padding: Style.dimens.pane_padding @@ -47,7 +50,6 @@ id: titleText Accessible.ignored: true - activeFocusOnTab: false textStyle: Style.text.subline visible: text !== "" } @@ -55,7 +57,6 @@ id: descriptionText Accessible.ignored: true - activeFocusOnTab: false visible: text !== "" } } @@ -69,8 +70,10 @@ } } - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) + Accessible.onScrollDownAction: Utils.scrollPageDownOnGFlickable(this) + Accessible.onScrollUpAction: Utils.scrollPageUpOnGFlickable(this) + onClicked: if (hasLink) + Qt.openUrlExternally(linkToOpen) StatefulColors { id: colors diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/GOptionsContainer.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/GOptionsContainer.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/GOptionsContainer.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/GOptionsContainer.qml 2025-10-30 10:10:48.000000000 +0000 @@ -4,30 +4,37 @@ import QtQuick import QtQuick.Layouts + import Governikus.Global import Governikus.Style ColumnLayout { - default property alias containerData: pane.paneData - property alias containerPadding: pane.padding - property alias containerSpacing: pane.spacing + id: root + + default property alias containerData: contentLayout.data + property alias containerPadding: contentLayout.anchors.margins + property alias containerSpacing: contentLayout.spacing property alias title: titleText.text spacing: Style.dimens.pane_spacing - GText { + Heading { id: titleText - Accessible.role: Accessible.Heading - textStyle: Style.text.headline + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + horizontalAlignment: Text.AlignLeft visible: text !== "" } - GPane { - id: pane - + GPaneBackground { Layout.fillWidth: true - color: Style.color.pane.background.basic - padding: 0 - spacing: 0 + implicitHeight: contentLayout.implicitHeight + contentLayout.anchors.bottomMargin + contentLayout.anchors.topMargin + implicitWidth: contentLayout.implicitWidth + contentLayout.anchors.leftMargin + contentLayout.anchors.rightMargin + + ColumnLayout { + id: contentLayout + + anchors.fill: parent + spacing: 0 + } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/GPane.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/GPane.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/GPane.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/GPane.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -/** - * Copyright (c) 2016-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick -import QtQuick.Layouts - -import Governikus.Global -import Governikus.Style - -GPaneBackground { - id: root - - property alias contentSpacing: paneContent.spacing - property alias horizontalTitleAlignment: titleText.horizontalAlignment - property int padding: Style.dimens.pane_padding - default property alias paneData: paneContent.data - property int spacing: Style.dimens.pane_spacing - property alias textStyle: titleText.textStyle - property alias title: titleText.text - - implicitHeight: content.implicitHeight + 2 * padding - implicitWidth: content.implicitWidth + 2 * padding - - ColumnLayout { - id: content - - spacing: root.spacing - - anchors { - fill: parent - margins: root.padding - } - PaneTitle { - id: titleText - - Layout.fillWidth: true - } - Column { - id: paneContent - - Layout.fillWidth: true - spacing: root.spacing - } - } -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/Hint.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/Hint.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/Hint.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/Hint.qml 2025-10-30 10:10:48.000000000 +0000 @@ -13,33 +13,43 @@ property alias buttonIconSource: hintButton.icon.source property alias buttonText: hintButton.text + property string linkToOpen property alias text: hintText.text signal clicked + signal linkAboutToOpen + Accessible.ignored: true color: Style.color.paneSublevel.background.basic + contentSpacing: 0 drawShadow: false - textStyle: Style.text.subline + titleTextStyle: Style.text.subline - ColumnLayout { - spacing: 0 - width: parent.width + GText { + id: hintText - GText { - id: hintText - - visible: text !== "" - } - GButton { - id: hintButton + visible: text !== "" + } + GButton { + id: hintButton - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: Style.dimens.groupbox_spacing - icon.source: "qrc:///images/open_website.svg" - tintIcon: hintText.color - visible: text !== "" + readonly property bool hasLink: root.linkToOpen !== "" - onClicked: root.clicked() + Accessible.description: hasLink ? Utils.platformAgnosticLinkOpenText(root.linkToOpen, Accessible.name) : "" + Accessible.role: hasLink ? Accessible.Link : Accessible.Button + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: Style.dimens.groupbox_spacing + icon.source: "qrc:///images/open_website.svg" + tintIcon: true + visible: text !== "" + + onClicked: { + if (hasLink) { + root.linkAboutToOpen(); + Qt.openUrlExternally(root.linkToOpen); + } else { + root.clicked(); + } } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/PaneTitle.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/PaneTitle.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/+mobile/PaneTitle.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/+mobile/PaneTitle.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/** - * Copyright (c) 2018-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick -import Governikus.Style - -GText { - Accessible.role: Accessible.TitleBar - elide: Text.ElideRight - textStyle: Style.text.headline - visible: text !== "" -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/BaseConfirmationPopup.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/BaseConfirmationPopup.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/BaseConfirmationPopup.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/BaseConfirmationPopup.qml 2025-10-30 10:10:48.000000000 +0000 @@ -49,7 +49,7 @@ anchors.centerIn: parent bottomMargin: parent ? 0.125 * parent.height : 0 - closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape + closePolicy: Popup.CloseOnPressOutside focus: true leftMargin: parent ? 0.125 * parent.width : 0 modal: true @@ -81,13 +81,15 @@ root.accept() RowLayout { - GText { + visible: root.title !== "" || root.showCloseButton + + Heading { + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft elide: Text.ElideRight focus: true horizontalAlignment: root.horizontalTextAlignment maximumLineCount: 5 text: root.title - textStyle: Style.text.headline visible: root.title !== "" } GSpacer { @@ -110,19 +112,12 @@ GText { id: mainText - Layout.maximumWidth: Number.POSITIVE_INFINITY - Layout.preferredWidth: Math.ceil(hiddenText.implicitWidth) + // Layout.maximumWidth is only required for desktop as long as the minimum Qt version is <= 6.8.1 + Layout.maximumWidth: Style.is_layout_desktop ? Number.POSITIVE_INFINITY : Math.ceil(implicitWidth) horizontalAlignment: root.horizontalTextAlignment text: root.text textFormat: Text.RichText visible: root.text !== "" - - GText { - id: hiddenText - - text: root.text - visible: false - } } Item { id: customContent diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/Crossed.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/Crossed.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/Crossed.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/Crossed.qml 2025-10-30 10:10:48.000000000 +0000 @@ -75,7 +75,7 @@ component Line: Rectangle { antialiasing: true color: Style.color.warning - height: Style.dimens.separator_size_large + height: 2 * Style.dimens.border_width width: d.c } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/DecisionView.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/DecisionView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/DecisionView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/DecisionView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -33,12 +33,9 @@ readonly property real buttonWidth: Math.max(agreeButton.implicitWidth, disagreeButton.implicitWidth) } - GText { + Heading { id: headline - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - textStyle: Style.text.headline visible: headline.text !== "" wrapMode: Text.WordWrap } @@ -48,11 +45,10 @@ Layout.alignment: Qt.AlignHCenter Layout.topMargin: Style.dimens.pane_spacing } - GText { + Subheading { id: subtitle Layout.topMargin: Style.dimens.pane_spacing - textStyle: Style.text.subline } Repeater { id: descriptionTexts diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/FormattedTextView.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/FormattedTextView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/FormattedTextView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/FormattedTextView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -9,88 +9,77 @@ import Governikus.Style import Governikus.Type -Item { +GPaneBackgroundDelegate { id: root - property alias color: delegate.color required property string content required property int index - property real maximumContentWidth: Number.POSITIVE_INFINITY - property alias totalItemCount: delegate.count required property int type + signal scrollDownAction + signal scrollUpAction + + Accessible.focusable: true Accessible.ignored: root.content === "" Accessible.name: ApplicationModel.stripHtmlTags(root.content) Accessible.role: { switch (root.type) { case FormattedTextModel.LineType.HEADER: - return Accessible.Heading; case FormattedTextModel.LineType.SECTION: case FormattedTextModel.LineType.SUBSECTION: - return Accessible.Section; - case FormattedTextModel.LineType.LISTITEM: - return Accessible.ListItem; + return Utils.useSpecialAppleTabRole(Accessible.Heading); default: - return Accessible.StaticText; + return Qt.platform.os === "windows" ? Accessible.Paragraph : Utils.useSpecialAppleTabRole(Accessible.StaticText); } } - implicitHeight: delegate.implicitHeight - z: 0 + idx: index + implicitHeight: row.implicitHeight - GPaneBackgroundDelegate { - id: delegate + Accessible.onScrollDownAction: root.scrollDownAction() + Accessible.onScrollUpAction: root.scrollUpAction() - anchors.centerIn: parent - anchors.horizontalCenterOffset: -Style.dimens.pane_padding / 2 - idx: root.index - implicitHeight: row.implicitHeight - width: Math.min(root.width - Style.dimens.pane_padding, root.maximumContentWidth) - - RowLayout { - id: row - - readonly property int horizontalPadding: Style.dimens.pane_padding - - anchors.fill: parent - - GText { - id: prefix - - Accessible.ignored: true - Layout.fillHeight: true - activeFocusOnTab: false - fontSizeMode: Text.Fit - leftPadding: row.horizontalPadding - text: "•" - textStyle: contentText.textStyle - verticalAlignment: Text.AlignTop - visible: root.type === FormattedTextModel.LineType.LISTITEM - } - GText { - id: contentText + RowLayout { + id: row + + readonly property int horizontalPadding: Style.dimens.pane_padding + + anchors.fill: parent + + GText { + id: prefix + + Accessible.ignored: true + Layout.fillHeight: true + fontSizeMode: Text.Fit + leftPadding: row.horizontalPadding + text: "•" + textStyle: contentText.textStyle + verticalAlignment: Text.AlignTop + visible: root.type === FormattedTextModel.LineType.LISTITEM + } + GText { + id: contentText - Accessible.ignored: true - Layout.maximumWidth: Number.POSITIVE_INFINITY - activeFocusOnTab: false - bottomPadding: delegate.isLast ? Style.dimens.pane_padding : 0 - font.underline: root.type === FormattedTextModel.LineType.SECTION - leftPadding: prefix.visible ? 0 : row.horizontalPadding - rightPadding: row.horizontalPadding - text: root.content - textStyle: { - switch (root.type) { - case FormattedTextModel.LineType.HEADER: - return Style.text.title; - case FormattedTextModel.LineType.SECTION: - return Style.text.headline; - case FormattedTextModel.LineType.SUBSECTION: - return Style.text.subline; - default: - return Style.text.normal; - } + Accessible.ignored: true + Layout.maximumWidth: Number.POSITIVE_INFINITY + bottomPadding: root.isLast ? Style.dimens.pane_padding : 0 + font.underline: root.type === FormattedTextModel.LineType.SECTION + leftPadding: prefix.visible ? 0 : row.horizontalPadding + rightPadding: row.horizontalPadding + text: root.content + textStyle: { + switch (root.type) { + case FormattedTextModel.LineType.HEADER: + return Style.text.title; + case FormattedTextModel.LineType.SECTION: + return Style.text.headline; + case FormattedTextModel.LineType.SUBSECTION: + return Style.text.subline; + default: + return Style.text.normal; } - topPadding: delegate.isFirst ? Style.dimens.pane_padding : 0 } + topPadding: root.isFirst ? Style.dimens.pane_padding : 0 } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GAbstractButton.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GAbstractButton.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GAbstractButton.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GAbstractButton.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Controls +import Governikus.Global + +AbstractButton { + Accessible.onScrollDownAction: Utils.scrollPageDownOnGFlickable(this) + Accessible.onScrollUpAction: Utils.scrollPageUpOnGFlickable(this) + Keys.onEnterPressed: clicked() + Keys.onReturnPressed: clicked() + onFocusChanged: if (focus) + Utils.positionViewAtItem(this) +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GButton.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GButton.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GButton.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GButton.qml 2025-10-30 10:10:48.000000000 +0000 @@ -10,7 +10,7 @@ import Governikus.Style import Governikus.View -AbstractButton { +GAbstractButton { id: root property int borderWidth: Style.dimens.border_width @@ -28,6 +28,7 @@ property TextStyle textStyle: Style.text.button property bool tintIcon: false + Accessible.ignored: !enableButton Accessible.name: text Layout.fillWidth: true Layout.maximumWidth: Math.ceil(implicitWidth) @@ -65,7 +66,7 @@ sourceSize.height: 1.2 * buttonText.effectiveFirstLineHeight tintColor: colors.controlContent tintEnabled: root.tintIcon - visible: source != "" + visible: source.toString() !== "" } GText { id: buttonText @@ -73,7 +74,6 @@ Accessible.ignored: true Layout.alignment: Qt.AlignHCenter Layout.maximumWidth: Number.POSITIVE_INFINITY - activeFocusOnTab: false color: colors.controlContent elide: Text.ElideRight font: root.font @@ -93,8 +93,6 @@ onActiveFocusOnTabChanged: if (!activeFocusOnTab) focus = false - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) HoverHandler { id: hoverHandler diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GCheckBox.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GCheckBox.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GCheckBox.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GCheckBox.qml 2025-10-30 10:10:48.000000000 +0000 @@ -25,8 +25,6 @@ contentItem: RowLayout { id: contentLayout - readonly property int focusWidth: layoutDirection === Qt.RightToLeft ? width : implicitWidth - spacing: root.spacing Rectangle { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GCollapsible.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GCollapsible.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GCollapsible.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GCollapsible.qml 2025-10-30 10:10:48.000000000 +0000 @@ -5,11 +5,11 @@ pragma ComponentBehavior: Bound import QtQuick -import QtQuick.Controls import QtQuick.Layouts import Governikus.Global import Governikus.Style import Governikus.View +import Governikus.Type ColumnLayout { id: root @@ -17,6 +17,7 @@ property bool alwaysReserveSelectionTitleHeight: false property bool arrowToLeft: false property alias backgroundColor: collapsibleContentBackground.color + readonly property alias content: contentItem.children property int contentBottomMargin: Style.dimens.groupbox_spacing property int contentHorizontalMargin: horizontalMargin property alias contentSpacing: contentItem.spacing @@ -24,35 +25,44 @@ property bool drawBottomCorners: false property bool drawTopCorners: false default property alias expandableData: contentItem.data - property bool expanded: false + readonly property alias expanded: expandButton.checked property int horizontalMargin: Style.dimens.pane_spacing property alias selectionIcon: selectionIcon.source property alias selectionTitle: selectionTitle.text + property bool startExpanded: false property alias tintIcon: selectionIcon.tintEnabled property alias title: title.text + function onOptionSelected() { + if (expandButton.checked && !ApplicationModel.screenReaderRunning) { + expandButton.checked = false; + } + } + spacing: 0 - AbstractButton { + GAbstractButton { id: expandButton - Accessible.name: root.title + ". " + - //: LABEL ANDROID IOS - (root.expanded ? qsTr("collapse") : - //: LABEL ANDROID IOS - qsTr("expand") + ". ") + - //: LABEL ANDROID IOS - (root.selectionTitle !== "" ? qsTr("Currently selected is %1").arg(root.selectionTitle) : "") - Accessible.role: Accessible.Button + //: LABEL ALL_PLATFORMS + Accessible.description: root.selectionTitle !== "" ? qsTr("Currently selected is %1").arg(root.selectionTitle) : "" + Accessible.name: root.title + Accessible.role: { + if ("Switch" in Accessible) { + return Accessible.Switch; // qmllint disable missing-property + } + return Accessible.Button; + } Layout.fillWidth: true + checkable: true implicitHeight: bannerLayout.implicitHeight + Style.dimens.pane_spacing * 2 implicitWidth: bannerLayout.implicitWidth background: RoundedRectangle { id: background - bottomLeftCorner: root.drawBottomCorners && !root.expanded - bottomRightCorner: root.drawBottomCorners && !root.expanded + bottomLeftCorner: root.drawBottomCorners && !expandButton.checked + bottomRightCorner: root.drawBottomCorners && !expandButton.checked color: colors.paneBackground topLeftCorner: root.drawTopCorners topRightCorner: root.drawTopCorners @@ -132,13 +142,15 @@ } } - onClicked: root.expanded = !root.expanded - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) + Accessible.onPressAction: expandButton.toggle() + Accessible.onScrollDownAction: Utils.scrollPageDownOnGFlickable(this) + Accessible.onScrollUpAction: Utils.scrollPageUpOnGFlickable(this) + Component.onCompleted: checked = root.startExpanded StatefulColors { id: colors + checkedCondition: false statefulControl: expandButton } } @@ -150,11 +162,11 @@ bottomRightCorner: root.drawBottomCorners clip: true color: Style.color.paneSublevel.background.basic - implicitHeight: root.expanded ? (contentItem.implicitHeight + contentItem.anchors.topMargin + contentItem.anchors.bottomMargin) : 0 + implicitHeight: expandButton.checked ? (contentItem.implicitHeight + contentItem.anchors.topMargin + contentItem.anchors.bottomMargin) : 0 implicitWidth: contentItem.implicitWidth + contentItem.anchors.leftMargin + contentItem.anchors.rightMargin topLeftCorner: false topRightCorner: false - visible: root.expanded + visible: expandButton.checked Behavior on implicitHeight { NumberAnimation { @@ -180,7 +192,7 @@ component LeftRightArrow: TintableIcon { Layout.leftMargin: root.horizontalMargin Layout.rightMargin: root.horizontalMargin - source: root.expanded ? "qrc:///images/material_expand_less.svg" : "qrc:///images/material_expand_more.svg" + source: expandButton.checked ? "qrc:///images/material_expand_less.svg" : "qrc:///images/material_expand_more.svg" sourceSize.height: Style.text.normal.textSize tintColor: Style.color.textNormal.basic tintEnabled: true diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GColorOverlay.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GColorOverlay.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GColorOverlay.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GColorOverlay.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -/** - * Copyright (c) 2024-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick.Effects - -MultiEffect { - brightness: 0.5 - colorization: 1 - contrast: -1.0 -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GComboBox.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GComboBox.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GComboBox.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GComboBox.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -/** - * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany - */ - -pragma ComponentBehavior: Bound - -import QtQuick -import QtQuick.Controls -import Governikus.View -import Governikus.Style -import Governikus.Type - -ComboBox { - id: root - - property int radius: Style.dimens.pane_radius - property TextStyle textStyle: Style.text.normal - - Accessible.name: displayText - Accessible.role: Accessible.ComboBox - font.pixelSize: textStyle.textSize - horizontalPadding: Style.dimens.control_horizontalPadding - leftPadding: horizontalPadding - popup.bottomMargin: UiPluginModel.safeAreaMargins.bottom - popup.leftMargin: UiPluginModel.safeAreaMargins.left - popup.rightMargin: UiPluginModel.safeAreaMargins.right - popup.topMargin: UiPluginModel.safeAreaMargins.top - rightPadding: horizontalPadding - spacing: Style.dimens.groupbox_spacing - verticalPadding: Style.dimens.control_verticalPadding - - background: GPaneBackground { - border.color: root.textStyle.textColor - color: Style.color.transparent - drawShadow: false - radius: root.radius - } - contentItem: GText { - activeFocusOnTab: false - elide: Text.ElideRight - font.pixelSize: root.font.pixelSize - maximumLineCount: 1 - rightPadding: root.indicator.width + root.spacing - text: root.displayText - textStyle: root.textStyle - } - delegate: ItemDelegate { - id: itemDelegate - - required property int index - required property string modelData - - highlighted: root.highlightedIndex === index - width: root.width - - background: Rectangle { - color: itemDelegate.highlighted ? Style.color.control.background.basic : Style.color.paneSublevel.background.basic - - GSeparator { - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right - } - } - } - contentItem: GText { - activeFocusOnTab: false - color: itemDelegate.highlighted ? Style.color.control.content.hovered : root.textStyle.textColor - elide: Text.ElideRight - font.pixelSize: root.font.pixelSize - text: itemDelegate.modelData - textStyle: root.textStyle - } - } - indicator: Item { - TintableIcon { - source: root.down ? "qrc:///images/material_expand_less.svg" : "qrc:///images/material_expand_more.svg" - sourceSize.height: root.font.pixelSize - tintColor: root.textStyle.textColor - x: Math.round(root.width - width - root.rightPadding) - y: Math.round(root.topPadding + (root.availableHeight - height) / 2) - } - } - - FocusFrame { - marginFactor: 1 - radius: 1.2 * root.radius - size: root.font.pixelSize / 8 - } -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GCrossBlendedText.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GCrossBlendedText.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GCrossBlendedText.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GCrossBlendedText.qml 2025-10-30 10:10:48.000000000 +0000 @@ -41,7 +41,6 @@ id: mainText Accessible.ignored: true - activeFocusOnTab: false anchors.fill: parent Behavior on text { @@ -93,7 +92,6 @@ id: tempText Accessible.ignored: true - activeFocusOnTab: false color: mainText.color elide: mainText.elide font: mainText.font diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GDesaturate.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GDesaturate.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GDesaturate.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GDesaturate.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -/** - * Copyright (c) 2024-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick.Effects - -MultiEffect { - saturation: -1 -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GFlickable.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GFlickable.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GFlickable.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GFlickable.qml 2025-10-30 10:10:48.000000000 +0000 @@ -7,19 +7,81 @@ import Governikus.Global import Governikus.Style +import Governikus.Type Flickable { id: root - function handleKeyPress(key) { - if (key === Qt.Key_PageDown || key === Qt.Key_Down) + function findFocusableChild(pChildren, pScreenReaderRunning) { + const isItemVisible = pItem => { + if (!pItem.visible || !pItem.height) { + return false; + } + + const itemTop = pItem.mapToItem(contentItem, 0, 0).y; + const itemBottom = itemTop + pItem.height; + const viewTop = contentY; + const viewBottom = viewTop + height; + + return itemTop >= viewTop && itemBottom <= viewBottom; + }; + + const isFocusable = (pItem, pScreenReaderRunning) => { + if (!pItem) { + return false; + } + + if (pScreenReaderRunning) { + return pItem.Accessible && pItem.Accessible.focusable && !pItem.Accessible.ignored; + } + + return pItem.activeFocusOnTab; + }; + + for (const child of pChildren) { + if (isItemVisible(child) && isFocusable(child, pScreenReaderRunning)) { + return child; + } + if (child.children) { + const nested = findFocusableChild(child.children, pScreenReaderRunning); + if (nested) { + return nested; + } + } + } + return null; + } + function focusElementAfterScroll() { + const screenReaderRunning = ApplicationModel.screenReaderRunning; + const item = findFocusableChild(contentItem.children, screenReaderRunning); + item?.forceActiveFocus(screenReaderRunning ? Qt.MouseFocusReason : Qt.TabFocusReason); + } + function handleKeyPress(event) { + switch (event.key) { + case Qt.Key_Down: + if (ApplicationModel.screenReaderRunning) + return; + // fall through + case Qt.Key_PageDown: root.scrollPageDown(); - else if (key === Qt.Key_PageUp || key === Qt.Key_Up) + break; + case Qt.Key_Up: + if (ApplicationModel.screenReaderRunning) + return; + // fall through + case Qt.Key_PageUp: root.scrollPageUp(); - else if (key === Qt.Key_End) + break; + case Qt.Key_End: root.contentY = root.contentHeight - root.height; - else if (key === Qt.Key_Home) + break; + case Qt.Key_Home: root.contentY = root.originY; + break; + default: + return; + } + event.accepted = true; } function highlightScrollbar() { if (ScrollBar.vertical) @@ -28,23 +90,13 @@ function positionViewAtBeginning() { contentY = originY; } - function positionViewAtItem(pItem) { - let item = pItem.parent ? pItem.parent : pItem; - let mappedPosition = this.mapFromItem(item, pItem.x, pItem.y); - if (mappedPosition.y < 0) { - contentY = Math.max(0, contentY + mappedPosition.y); - return; - } - let viewDifference = mappedPosition.y + pItem.height - height; - if (viewDifference > 0) { - contentY = Math.min(contentY + viewDifference, contentHeight - height); - } - } function scrollPageDown() { scrollBar.increase(); + focusElementAfterScroll(); } function scrollPageUp() { scrollBar.decrease(); + focusElementAfterScroll(); } Accessible.focusable: false @@ -67,7 +119,7 @@ Accessible.onScrollDownAction: scrollPageDown() Accessible.onScrollUpAction: scrollPageUp() Keys.onPressed: event => { - handleKeyPress(event.key); + handleKeyPress(event); } onVisibleChanged: if (visible) highlightScrollbar() diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GInformativeButton.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GInformativeButton.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GInformativeButton.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GInformativeButton.qml 2025-10-30 10:10:48.000000000 +0000 @@ -2,12 +2,11 @@ * Copyright (c) 2022-2025 Governikus GmbH & Co. KG, Germany */ import QtQuick -import QtQuick.Controls import QtQuick.Layouts import Governikus.Style import Governikus.View -AbstractButton { +GAbstractButton { id: root property alias description: descriptionText.text @@ -47,7 +46,6 @@ id: title Accessible.ignored: true - activeFocusOnTab: false color: root.isPane ? colors.textSubline : colors.controlContent elide: Text.ElideRight text: root.text @@ -57,7 +55,6 @@ id: descriptionText Accessible.ignored: true - activeFocusOnTab: false color: root.isPane ? colors.textNormal : colors.controlContent elide: Text.ElideRight visible: text !== "" @@ -70,9 +67,6 @@ } } - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) - HoverHandler { id: hoverHandler diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GLink.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GLink.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GLink.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GLink.qml 2025-10-30 10:10:48.000000000 +0000 @@ -2,14 +2,13 @@ * Copyright (c) 2016-2025 Governikus GmbH & Co. KG, Germany */ import QtQuick -import QtQuick.Controls import QtQuick.Layouts import Governikus.Global import Governikus.Style import Governikus.View import Governikus.Type -AbstractButton { +GAbstractButton { id: root property alias colorStyle: colors.linkStyle @@ -25,6 +24,7 @@ Layout.maximumWidth: Math.ceil(implicitWidth) background: null font.pixelSize: linkText.textStyle.textSize + font.underline: UiPluginModel.a11yButtonShapeActive font.weight: linkText.textStyle.fontWeight horizontalPadding: Style.dimens.control_horizontalPadding verticalPadding: Style.dimens.control_verticalPadding @@ -44,7 +44,7 @@ source: root.icon.source sourceSize.height: 1.5 * linkText.effectiveFirstLineHeight tintColor: colors.linkColor - visible: root.icon.source != "" + visible: root.icon.source.toString() !== "" Behavior on source { enabled: SettingsModel.useAnimations && !Style.is_layout_desktop @@ -75,7 +75,6 @@ Accessible.ignored: true Layout.alignment: Qt.AlignHCenter Layout.maximumWidth: Number.POSITIVE_INFINITY - activeFocusOnTab: false color: colors.linkColor elide: Text.ElideRight font: root.font @@ -96,8 +95,6 @@ onActiveFocusOnTabChanged: if (!activeFocusOnTab) focus = false - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) HoverHandler { id: hoverHandler diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GListView.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GListView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GListView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GListView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,34 +14,82 @@ property alias scrollBarColor: scrollBar.color property real scrollBarTopPadding: 0 - function handleKeyPress(key) { - if (key === Qt.Key_PageDown) + function firstIndexInView() { + const firstIndex = indexAt(0, contentY); + return firstIndex === -1 ? 0 : firstIndex; + } + function handleItemFocused(pIndex) { + positionViewAtIndex(pIndex, ListView.Center); + currentIndex = pIndex; + } + function handleKeyPress(event) { + if (root.contentHeight <= root.height) + return; + + let key = event.key; + if (key === Qt.Key_PageDown) { + if (root.atYEnd) + return; root.scrollPageDown(); - else if (key === Qt.Key_PageUp) + root.setIndexToCurrentlyCenteredItem(); + event.accepted = true; + } else if (key === Qt.Key_PageUp) { + if (root.atYBeginning) + return; root.scrollPageUp(); - else if (key === Qt.Key_End) + root.setIndexToCurrentlyCenteredItem(); + event.accepted = true; + } else if (key === Qt.Key_End) { root.positionViewAtEnd(); - else if (key === Qt.Key_Home) + root.currentIndex = count - 1; + event.accepted = true; + } else if (key === Qt.Key_Home) { root.positionViewAtBeginning(); + root.currentIndex = 0; + event.accepted = true; + } } function highlightScrollbar() { if (ScrollBar.vertical) (ScrollBar.vertical as GScrollBar).highlight(); } + function isListElementEmptyFunc(pItem) { + return false; + } + function lastIndexInView() { + const lastIndex = indexAt(0, height + contentY); + return lastIndex === -1 ? count - 1 : lastIndex; + } function scrollPageDown() { scrollBar.increase(); } function scrollPageUp() { scrollBar.decrease(); } + function setIndexToCurrentlyCenteredItem() { + let index = indexAt(1, contentY + height / 2); + if (index === -1) + return; - Accessible.ignored: true + var item = itemAtIndex(index); + while (isListElementEmptyFunc(item)) { + index++; + if (index === count - 1) + return; + item = itemAtIndex(index); + } + currentIndex = index; + } + + Accessible.role: Accessible.List + activeFocusOnTab: true boundsBehavior: Style.is_layout_desktop ? Flickable.StopAtBounds : (contentHeight <= height ? Flickable.StopAtBounds : Flickable.DragAndOvershootBounds) boundsMovement: Flickable.FollowBoundsBehavior flickDeceleration: Style.flickDeceleration flickableDirection: Flickable.VerticalFlick + highlightMoveDuration: 0 + highlightResizeDuration: 0 maximumFlickVelocity: Style.scrolling_speed - reuseItems: true ScrollBar.vertical: GScrollBar { id: scrollBar @@ -55,8 +103,18 @@ Accessible.onIncreaseAction: scrollPageDown() Accessible.onScrollDownAction: scrollPageDown() Accessible.onScrollUpAction: scrollPageUp() + Keys.onDownPressed: { + do { + root.incrementCurrentIndex(); + } while (isListElementEmptyFunc(currentItem) && root.currentIndex + 1 < root.count) + } Keys.onPressed: event => { - handleKeyPress(event.key); + handleKeyPress(event); + } + Keys.onUpPressed: { + do { + root.decrementCurrentIndex(); + } while (isListElementEmptyFunc(currentItem) && root.currentIndex > 1) } onVisibleChanged: if (visible) highlightScrollbar() diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GPane.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GPane.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GPane.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GPane.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2016-2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick +import QtQuick.Layouts + +import Governikus.Global +import Governikus.Style + +GPaneBackground { + id: root + + property int contentPadding: Style.dimens.pane_padding + property alias contentSpacing: paneContent.spacing + default property alias data: paneContent.data + property alias spacing: containerCol.spacing + property alias title: titleText.text + property int titleMargins: Style.dimens.pane_padding + property alias titleTextStyle: titleText.textStyle + + Accessible.focusable: true + Accessible.ignored: title === "" + Accessible.name: title + Accessible.role: Accessible.Grouping + Layout.maximumHeight: containerCol.Layout.maximumHeight + implicitHeight: containerCol.implicitHeight + implicitWidth: containerCol.implicitWidth + + onFocusChanged: if (focus) + Utils.positionViewAtItem(this) + + ColumnLayout { + id: containerCol + + anchors.fill: parent + spacing: 0 + + PaneTitle { + id: titleText + + Layout.leftMargin: root.titleMargins + Layout.rightMargin: root.titleMargins + Layout.topMargin: root.titleMargins + } + ColumnLayout { + id: paneContent + + Layout.bottomMargin: root.contentPadding + Layout.leftMargin: root.contentPadding + Layout.maximumWidth: Number.POSITIVE_INFINITY + Layout.rightMargin: root.contentPadding + Layout.topMargin: titleText.visible ? Style.dimens.pane_spacing : root.contentPadding + spacing: Style.dimens.pane_spacing + } + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GProgressBar.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GProgressBar.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GProgressBar.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GProgressBar.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,6 +1,7 @@ /** * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany */ + import QtQuick import QtQuick.Controls import Governikus.Global @@ -12,24 +13,41 @@ id: root readonly property alias effectiveVisualPosition: bar.mutableVisualPosition - property alias text: progressText.text + property string text: "" + + Accessible.focusable: true - Accessible.name: qsTr("%1 percent done").arg(value) + //: LABEL ALL_PLATFORMS + Accessible.name: qsTr("Progress") Accessible.role: Accessible.ProgressBar background: null from: 0 to: 100 contentItem: Rectangle { + Accessible.ignored: true border.color: Style.color.control.border.basic - border.width: Style.dimens.progress_bar_border + border.width: 2 * Style.dimens.border_width color: Style.color.background - implicitHeight: Style.dimens.progress_bar_height + implicitHeight: 2 * (progressText.font.pixelSize + border.width) + implicitWidth: Math.max(10 * implicitHeight, progressText.implicitWidth + implicitHeight) radius: height / 2 + GText { + id: progressText + + Accessible.ignored: true + anchors.centerIn: parent + elide: Text.ElideMiddle + font.weight: Style.font.bold + maximumLineCount: 1 + text: root.text + } Item { + id: barSpace + anchors.fill: parent - anchors.margins: Style.dimens.progress_bar_border * 3 + anchors.margins: 3 * parent.border.width Rectangle { id: bar @@ -38,6 +56,7 @@ border.color: Style.color.control.border.basic border.width: Style.dimens.border_width + clip: true color: Style.color.control.background.basic height: parent.height radius: height / 2 @@ -50,21 +69,21 @@ velocity: 0.5 } } - } - } - GText { - id: progressText - color: Style.color.progressbar_text - elide: Text.ElideMiddle - font.weight: Font.Bold - horizontalAlignment: Text.AlignHCenter - maximumLineCount: 1 - - anchors { - left: parent.left - right: parent.right - verticalCenter: parent.verticalCenter + GText { + Accessible.ignored: true + color: Style.color.control.content.basic + elide: Text.ElideMiddle + font.weight: Style.font.bold + maximumLineCount: 1 + text: root.text + + anchors { + left: parent.left + leftMargin: progressText.x - barSpace.anchors.margins + verticalCenter: parent.verticalCenter + } + } } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GRadioButton.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GRadioButton.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GRadioButton.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GRadioButton.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -/** - * Copyright (c) 2018-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Governikus.Global -import Governikus.Style -import Governikus.View - -RadioButton { - id: root - - property bool drawBottomCorners: false - property bool drawTopCorners: false - readonly property int indicatorHeight: Style.dimens.small_icon_size - property bool tintIcon: false - - Accessible.name: text - horizontalPadding: Style.dimens.pane_spacing - indicator: null - spacing: Style.dimens.groupbox_spacing - verticalPadding: Style.dimens.pane_spacing / 2 - - background: RoundedRectangle { - bottomLeftCorner: root.drawBottomCorners - bottomRightCorner: root.drawBottomCorners - color: colors.controlPreferredPaneBackground - topLeftCorner: root.drawTopCorners - topRightCorner: root.drawTopCorners - } - contentItem: RowLayout { - spacing: root.spacing - - Rectangle { - border.color: colors.controlBorder - border.width: Style.dimens.border_width - color: colors.controlBackground - implicitHeight: root.indicatorHeight - implicitWidth: root.indicatorHeight - radius: height / 2 - - Rectangle { - anchors.centerIn: parent - color: colors.controlContent - height: parent.height / 2.5 - radius: height / 2 - visible: root.checked - width: height - } - } - TintableIcon { - Layout.preferredHeight: sourceSize.height - source: root.icon.source - sourceSize.height: root.indicatorHeight - tintColor: description.color - tintEnabled: root.tintIcon - visible: source.toString() !== "" - } - GText { - id: description - - Accessible.ignored: true - activeFocusOnTab: false - text: root.text - visible: text !== "" - } - GSpacer { - Layout.fillWidth: true - visible: root.text !== "" - } - } - - HoverHandler { - id: hoverHandler - - } - StatefulColors { - id: colors - - controlStyle: Style.color.controlRadiobutton - groupMember: true - hoveredCondition: hoverHandler.hovered - statefulControl: root - } - FocusFrame { - anchors { - bottomMargin: root.bottomPadding / 2 - leftMargin: root.leftPadding / 2 - rightMargin: Math.max(0, root.contentItem.width - root.contentItem.implicitWidth) + root.rightPadding / 2 - topMargin: root.topPadding / 2 - } - } -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GSeparator.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GSeparator.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GSeparator.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GSeparator.qml 2025-10-30 10:10:48.000000000 +0000 @@ -8,6 +8,6 @@ property int orientation: Qt.Horizontal // Qt.Vertical color: Style.color.border - implicitHeight: orientation === Qt.Horizontal ? Style.dimens.separator_size : 0 - implicitWidth: orientation === Qt.Vertical ? Style.dimens.separator_size : 0 + implicitHeight: orientation === Qt.Horizontal ? Style.dimens.border_width : 0 + implicitWidth: orientation === Qt.Vertical ? Style.dimens.border_width : 0 } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GStagedProgressBar.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GStagedProgressBar.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GStagedProgressBar.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GStagedProgressBar.qml 2025-10-30 10:10:48.000000000 +0000 @@ -21,7 +21,7 @@ Accessible.name: qsTr("Step %1 of %2. This step is %3 percent complete.").arg(d.currentStage).arg(d.stages).arg(Math.floor(d.absoluteProgress * 100)) Accessible.role: Accessible.ProgressBar color: Style.color.pane.background.basic - implicitHeight: bars.implicitHeight + UiPluginModel.safeAreaMargins.bottom + implicitHeight: bars.implicitHeight layer.enabled: GraphicsInfo.api !== GraphicsInfo.Software layer.effect: GDropShadow { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GSwitch.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GSwitch.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GSwitch.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GSwitch.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,22 +1,34 @@ /** * Copyright (c) 2017-2025 Governikus GmbH & Co. KG, Germany */ + +pragma ComponentBehavior: Bound + import QtQuick -import QtQuick.Layouts import QtQuick.Controls +import QtQuick.Layouts + import Governikus.Style +import Governikus.Type import Governikus.View -Switch { +AbstractButton { id: root property alias description: descriptionText.text property bool drawBottomCorners: false property bool drawTopCorners: false - Accessible.name: text + ". " + description + Accessible.description: description + Accessible.name: text + Accessible.role: { + if ("Switch" in Accessible) { + return Accessible.Switch; // qmllint disable missing-property + } + return Accessible.CheckBox; + } + checkable: true horizontalPadding: Style.dimens.pane_spacing - indicator: null verticalPadding: Style.dimens.pane_spacing background: RoundedRectangle { @@ -27,11 +39,6 @@ topRightCorner: root.drawTopCorners } contentItem: RowLayout { - id: switchContent - - readonly property int focusWidth: layoutDirection === Qt.LeftToRight ? width : implicitWidth - - layoutDirection: Style.is_layout_desktop ? Qt.RightToLeft : Qt.LeftToRight spacing: Style.dimens.pane_spacing ColumnLayout { @@ -43,7 +50,6 @@ Accessible.ignored: true Layout.maximumWidth: Number.POSITIVE_INFINITY - activeFocusOnTab: false text: root.text textStyle: Style.text.subline visible: text !== "" @@ -53,42 +59,18 @@ Accessible.ignored: true Layout.maximumWidth: Number.POSITIVE_INFINITY - activeFocusOnTab: false visible: text !== "" } } - Rectangle { - border.color: colors.controlBorder - color: colors.controlBackground - implicitHeight: implicitWidth / 2 - implicitWidth: Style.dimens.switch_width - radius: height / 2 - - Rectangle { - id: ball - - readonly property int distanceBallBorder: 3 - - anchors.verticalCenter: parent.verticalCenter - color: colors.controlContent - height: parent.height - 2 * distanceBallBorder - radius: height / 2 - width: height - x: root.checked ? parent.width - width - distanceBallBorder : distanceBallBorder - - Behavior on x { - enabled: hoverHandler.hovered - - NumberAnimation { - duration: 200 - easing.type: Easing.InOutQuad - } - } - } + ToggleBox { + Layout.alignment: Qt.AlignTop } } - Accessible.onPressAction: toggle() + Accessible.onPressAction: if (enabled) + toggle() + Accessible.onScrollDownAction: Utils.scrollPageDownOnGFlickable(this) + Accessible.onScrollUpAction: Utils.scrollPageUpOnGFlickable(this) onFocusChanged: if (focus) Utils.positionViewAtItem(this) @@ -107,8 +89,62 @@ anchors { bottomMargin: root.bottomPadding / 2 leftMargin: root.leftPadding / 2 - rightMargin: Math.max(0, switchContent.width - switchContent.focusWidth) + root.rightPadding / 2 + rightMargin: root.rightPadding / 2 topMargin: root.topPadding / 2 } } + + component ToggleBox: Rectangle { + border.color: colors.controlBorder + color: colors.controlBackground + implicitHeight: implicitWidth / 2 + implicitWidth: Style.dimens.switch_width + radius: height / 2 + + TintableIcon { + source: "qrc:///images/material_check.svg" + sourceSize.height: parent.height * 0.65 + tintColor: ball.color + visible: UiPluginModel.a11yOnOffSwitchLabelActive && root.checked + + anchors { + left: parent.left + leftMargin: 2 * ball.distanceBallBorder + verticalCenter: parent.verticalCenter + } + } + TintableIcon { + source: "qrc:///images/material_close.svg" + sourceSize.height: parent.height * 0.4 + tintColor: ball.color + visible: UiPluginModel.a11yOnOffSwitchLabelActive && !root.checked + + anchors { + right: parent.right + rightMargin: 3 * ball.distanceBallBorder + verticalCenter: parent.verticalCenter + } + } + Rectangle { + id: ball + + readonly property int distanceBallBorder: 3 + + anchors.verticalCenter: parent.verticalCenter + color: colors.controlContent + height: parent.height - 2 * distanceBallBorder + radius: height / 2 + width: height + x: root.checked ? parent.width - width - distanceBallBorder : distanceBallBorder + + Behavior on x { + enabled: hoverHandler.hovered + + NumberAnimation { + duration: 200 + easing.type: Easing.InOutQuad + } + } + } + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GText.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GText.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GText.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GText.qml 2025-10-30 10:10:48.000000000 +0000 @@ -25,15 +25,14 @@ } } + Accessible.description: hasLink ? Utils.platformAgnosticLinkOpenText(link, Accessible.name) : "" Accessible.focusable: true Accessible.ignored: text === "" - Accessible.name: ApplicationModel.stripHtmlTags(text) + (Style.is_layout_desktop && hasLink ? - //: INFO DESKTOP Text read by screen reader if the text contains a weblink which may be opened. - " %1: %2".arg(qsTr("Press space to open link")).arg(d.link) : "") - Accessible.role: Style.is_layout_desktop && hasLink ? Accessible.Button : Accessible.StaticText + Accessible.name: ApplicationModel.stripHtmlTags(text) + Accessible.role: hasLink ? Accessible.Link : Accessible.StaticText Layout.fillWidth: true Layout.maximumWidth: Math.ceil(implicitWidth) - activeFocusOnTab: hasLink || ApplicationModel.isScreenReaderRunning + activeFocusOnTab: hasLink color: textStyle.textColor font.pixelSize: textStyle.textSize font.weight: textStyle.fontWeight @@ -43,7 +42,12 @@ verticalAlignment: Text.AlignVCenter wrapMode: d.nonMultilineElided ? Text.NoWrap : Text.Wrap + Accessible.onPressAction: tryActivateLink() + Accessible.onScrollDownAction: Utils.scrollPageDownOnGFlickable(this) + Accessible.onScrollUpAction: Utils.scrollPageUpOnGFlickable(this) Component.onCompleted: d.checkForLinks() + Keys.onEnterPressed: tryActivateLink() + Keys.onReturnPressed: tryActivateLink() Keys.onSpacePressed: tryActivateLink() onFocusChanged: if (focus) Utils.positionViewAtItem(this) diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/GTextField.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/GTextField.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/GTextField.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/GTextField.qml 2025-10-30 10:10:48.000000000 +0000 @@ -27,11 +27,16 @@ background: Rectangle { border.color: Style.color.border - border.width: Style.dimens.separator_size + border.width: Style.dimens.border_width color: Style.color.pane.background.basic radius: Style.dimens.control_radius } + Accessible.onScrollDownAction: Utils.scrollPageDownOnGFlickable(this) + Accessible.onScrollUpAction: Utils.scrollPageUpOnGFlickable(this) + onActiveFocusChanged: if (activeFocus) + Utils.positionViewAtItem(this, this.parent, Qt.platform.os === "android") + FocusFrame { scope: root } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/Heading.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/Heading.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/Heading.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/Heading.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick +import QtQuick.Layouts + +import Governikus.Style + +BaseHeading { + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + textStyle: Style.text.headline +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/LabeledText.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/LabeledText.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/LabeledText.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/LabeledText.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,9 +1,9 @@ /** * Copyright (c) 2016-2025 Governikus GmbH & Co. KG, Germany */ + import QtQuick import Governikus.Style -import Governikus.Type import Governikus.View Item { @@ -23,10 +23,12 @@ Accessible.name: labelText.text + d.effectiveSeparator + bodyText.text Accessible.role: Accessible.StaticText - activeFocusOnTab: ApplicationModel.isScreenReaderRunning implicitHeight: column.height implicitWidth: Math.max(labelText.implicitWidth, bodyText.implicitWidth) + Accessible.onScrollDownAction: Utils.scrollPageDownOnGFlickable(this) + Accessible.onScrollUpAction: Utils.scrollPageUpOnGFlickable(this) + QtObject { id: d @@ -50,7 +52,6 @@ id: labelText Accessible.ignored: true - activeFocusOnTab: false horizontalAlignment: root.alignment text: root.label textStyle: Style.text.subline @@ -60,8 +61,7 @@ GText { id: bodyText - Accessible.ignored: !hasLink - activeFocusOnTab: hasLink + Accessible.ignored: true horizontalAlignment: root.alignment text: root.text visible: !!text diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/NumberField.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/NumberField.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/NumberField.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/NumberField.qml 2025-10-30 10:10:48.000000000 +0000 @@ -15,14 +15,8 @@ Control { id: root - readonly property real eyeWidth: eye.width + eye.Layout.leftMargin + eye.Layout.rightMargin property alias number: echoField.text property alias passwordLength: echoField.maximumLength - - //: LABEL DESKTOP Screenreader text for the password field - readonly property string passwordState: qsTr("You entered %1 of %2 digits.").arg(number.length).arg(passwordLength) - readonly property var text: if (Qt.platform.os === "windows") - passwordState readonly property bool validInput: echoField.acceptableInput signal accepted @@ -31,6 +25,10 @@ echoField.insert(echoField.length, number); } function handleKeyEvent(eventKey, eventModifiers = Qt.NoModifier) { + if (!grid.focus) + return false; + if (Qt.platform.os === "android" && (eventKey & Qt.KeypadModifier) === Qt.KeypadModifier) + eventKey = eventKey & ~Qt.KeypadModifier; if (eventKey >= Qt.Key_0 && eventKey <= Qt.Key_9) { root.append(eventKey - Qt.Key_0); } else if (eventKey === Qt.Key_Backspace) { @@ -41,31 +39,18 @@ echoField.paste(); } else if ((eventKey === Qt.Key_Enter || eventKey === Qt.Key_Return) && validInput) { root.accepted(); - return true; } else { return false; } - - // Otherwise focus is lost if last clicked button gets invisible - // like 'C' in NumberPad. - if (visible) - root.forceActiveFocus(); return true; } function removeLast() { echoField.remove(echoField.length - 1, echoField.length); } - Accessible.name: (eye.activated ? - //: LABEL DESKTOP Screenreader text for the password field - qsTr("The number is visible. Digits entered so far: %1").arg(root.number.split("").join(" ")) : - //: LABEL DESKTOP Screenreader text for the password field - qsTr("The number is hidden.")) + (text === undefined ? " " + passwordState : "") - Accessible.role: Accessible.StaticText Layout.maximumWidth: contentItem.Layout.maximumWidth + leftPadding + rightPadding Layout.minimumWidth: contentItem.Layout.minimumWidth + leftPadding + rightPadding Layout.preferredWidth: implicitWidth - activeFocusOnTab: true contentItem: RowLayout { id: layout @@ -77,10 +62,22 @@ id: grid readonly property int markerWidth: Math.ceil(fontMetrics.averageCharacterWidth * 1.4) - + //: LABEL ALL_PLATFORMS Screenreader text for the password field + readonly property string passwordState: qsTr("You entered %1 of %2 digits.").arg(root.number.length).arg(root.passwordLength) + readonly property var text: if (Qt.platform.os === "windows") + passwordState + + Accessible.focusable: true + Accessible.name: (button.showNumber ? + //: LABEL ALL_PLATFORMS Screenreader text for the password field + qsTr("The number is visible. Digits entered so far: %1").arg(root.number.split("").join(" ")) : + //: LABEL ALL_PLATFORMS Screenreader text for the password field + qsTr("The number is hidden.")) + (text === undefined ? " " + passwordState : "") + Accessible.role: Accessible.EditableText Layout.maximumWidth: Layout.preferredWidth Layout.minimumWidth: markerWidth Layout.preferredWidth: markerWidth + (markerWidth + columnSpacing) * Math.max(5, root.passwordLength - 1) + activeFocusOnTab: true columnSpacing: Style.is_layout_desktop ? Style.dimens.pane_spacing : 5 columns: Math.max(1, Math.min(1 + (width - markerWidth) / (markerWidth + columnSpacing), root.passwordLength)) rowSpacing: columnSpacing @@ -103,11 +100,11 @@ color: Style.color.textNormal.basic font: fontMetrics.font horizontalAlignment: Text.AlignHCenter - text: eye.activated ? root.number.substr(digit.index, 1) : "" + text: button.showNumber ? root.number.substr(digit.index, 1) : "" verticalAlignment: Text.AlignTop Rectangle { - readonly property int normalHeight: Style.is_layout_desktop ? Math.max(UiPluginModel.scaleFactor * 4, 1) : 1 + readonly property int normalHeight: Style.is_layout_desktop ? Style.dimens.border_width * 2 : 1 color: parent.color height: digit.index === root.number.length ? normalHeight * 3 : normalHeight @@ -122,7 +119,7 @@ color: parent.color height: width radius: height / 2 - visible: !eye.activated && root.number.charAt(digit.index) !== "" + visible: !button.showNumber && root.number.charAt(digit.index) !== "" width: fontMetrics.averageCharacterWidth anchors { @@ -134,27 +131,31 @@ } } Button { - id: eye + id: button - property bool activated: false + property bool showNumber: false background: null padding: Style.dimens.text_spacing / 2 - text: (activated ? - //: LABEL DESKTOP Screenreader text for the eye icon to change the password visibility - qsTr("Press to hide the number") : - //: LABEL DESKTOP Screenreader text for the eye icon to change the password visibility - qsTr("Press to show the number")) + text: (showNumber ? (Style.is_layout_desktop ? + //: LABEL DESKTOP Screenreader text for the eye icon to change the password visibility + qsTr("Click to hide the number") : + //: LABEL ANDROID IOS Screenreader text for the eye icon to change the password visibility + qsTr("Tap to hide the number")) : (Style.is_layout_desktop ? + //: LABEL DESKTOP Screenreader text for the eye icon to change the password visibility + qsTr("Click to show the number") : + //: LABEL ANDROID IOS Screenreader text for the eye icon to change the password visibility + qsTr("Tap to show the number"))) contentItem: TintableIcon { - source: eye.activated ? "qrc:///images/eye_visibility_on.svg" : "qrc:///images/eye_visibility_off.svg" + source: button.showNumber ? "qrc:///images/eye_visibility_on.svg" : "qrc:///images/eye_visibility_off.svg" sourceSize.height: Style.is_layout_desktop ? Style.dimens.icon_size : Style.dimens.small_icon_size tintColor: Style.color.textNormal.basic } - onClicked: eye.activated = !eye.activated + onClicked: showNumber = !showNumber onVisibleChanged: if (visible) - activated = false + showNumber = false MouseArea { acceptedButtons: Qt.NoButton @@ -174,8 +175,8 @@ FontMetrics { id: fontMetrics - font.pixelSize: Style.is_layout_desktop ? UiPluginModel.scaleFactor * 50 : 24 - font.weight: Font.Bold + font.pixelSize: Style.is_layout_desktop ? UiPluginModel.scaleFactor * 30 : 24 + font.weight: Style.font.bold } TextInput { id: echoField @@ -189,6 +190,7 @@ } FocusFrame { framee: layout + scope: grid z: 1 MouseArea { @@ -197,7 +199,7 @@ cursorShape: Qt.PointingHandCursor onClicked: mouse => { - root.forceActiveFocus(); + grid.forceActiveFocus(); if (mouse.button === Qt.RightButton || mouse.button === Qt.MiddleButton) { echoField.paste(); } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/PaneTitle.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/PaneTitle.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/PaneTitle.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/PaneTitle.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2018-2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick + +import Governikus.Style + +BaseHeading { + elide: Text.ElideRight + maximumLineCount: { + if (Style.is_layout_desktop) { + return 1; + } + } + textStyle: Style.is_layout_desktop ? Style.text.subline : Style.text.headline + visible: text !== "" +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/PkiSwitch.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/PkiSwitch.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/PkiSwitch.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/PkiSwitch.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,8 +1,11 @@ /** * Copyright (c) 2021-2025 Governikus GmbH & Co. KG, Germany */ + import QtQml import QtQuick + +import Governikus.Style import Governikus.Type MouseArea { @@ -16,16 +19,21 @@ case 7: case 8: case 9: - //: INFO ANDROID IOS Used in notifications when the user taps the icon - ApplicationModel.showFeedback(qsTr("%1 more presses to toggle the environment (prod/test) for integrated functions.").arg(10 - d.counter), true); + if (Style.is_layout_desktop) { + //: INFO DESKTOP Used in notifications when the user taps the icon + ApplicationModel.showFeedback(qsTr("%1 more clicks to toggle the environment (prod/test) for integrated functions.").arg(10 - d.counter), true); + } else { + //: INFO ANDROID IOS Used in notifications when the user taps the icon + ApplicationModel.showFeedback(qsTr("%1 more taps to toggle the environment (prod/test) for integrated functions.").arg(10 - d.counter), true); + } break; case 10: SettingsModel.useSelfauthenticationTestUri = !SettingsModel.useSelfauthenticationTestUri; if (SettingsModel.useSelfauthenticationTestUri) { - //: INFO ANDROID IOS Used in notifications when the user taps the icon + //: INFO ALL_PLATFORMS Used in notifications when the user taps the icon ApplicationModel.showFeedback(qsTr("Testmode for the integrated functions activated."), true); } else { - //: INFO ANDROID IOS Used in notifications when the user taps the icon + //: INFO ALL_PLATFORMS Used in notifications when the user taps the icon ApplicationModel.showFeedback(qsTr("Testmode for the integrated functions deactivated."), true); } d.counter = 0; diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/PrivacyStatement.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/PrivacyStatement.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/PrivacyStatement.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/PrivacyStatement.qml 2025-10-30 10:10:48.000000000 +0000 @@ -5,12 +5,13 @@ import QtQuick import Governikus.Global +import Governikus.Style import Governikus.Type GText { readonly property string privacyStatementDescription: smart ? //: LABEL ALL_PLATFORMS Text of the Smart-eID html link inside of a sentence - qsTr("data privacy statement of the Federal Ministry of the Interior and Community") : + qsTr("data privacy statement of the Federal Ministry of the Interior") : //: LABEL ALL_PLATFORMS Text of the self authentication html link inside of a sentence qsTr("data privacy statement") readonly property string privacyStatementLink: "%2".arg(privacyStatementUrl).arg(privacyStatementDescription) @@ -22,7 +23,7 @@ readonly property string privacyStatementUrl: smart ? "https://www.ausweisapp.bund.de/%1/aa2/bmi/privacy".arg(SettingsModel.language) : "https://www.ausweisapp.bund.de/%1/aa2/privacy".arg(SettingsModel.language) property bool smart: false - font.weight: Font.Bold + font.weight: Style.font.bold text: privacyStatementText.arg(privacyStatementLink) wrapMode: Text.WordWrap } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/ProxyCredentialsPopup.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/ProxyCredentialsPopup.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/ProxyCredentialsPopup.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/ProxyCredentialsPopup.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -/** - * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick - -import Governikus.Global -import Governikus.Type -import Governikus.Style - -ConfirmationPopup { - id: root - - property ProxyCredentials credentials - - //: LABEL DESKTOP Text of the button in the proxy credentials popup. - okButtonText: qsTr("Sign in") - - //: LABEL DESKTOP Title of the proxy credentials popup. - title: qsTr("Sign in") - width: formGrid.width + 2 * Style.dimens.pane_padding - - onClosed: { - if (!credentials) - return; - credentials.confirmInput(); - } - onConfirmed: { - if (!credentials) - return; - credentials.user = userInput.text; - credentials.password = passwordInput.text; - } - onCredentialsChanged: { - //: LABEL DESKTOP Text of the proxy credentials popup. An example for %1 is http://proxy.example.com:1337. - text = qsTr("The proxy %1 requires username and password.").arg(credentials ? credentials.url : ""); - userInput.text = credentials ? credentials.proposedUser : ""; - passwordInput.text = ""; - } - - Grid { - id: formGrid - - columns: 2 - spacing: Style.dimens.pane_spacing - verticalItemAlignment: Text.AlignVCenter - - GText { - //: LABEL DESKTOP Accessible name. - Accessible.name: qsTr("Proxy credential username") - - //: LABEL DESKTOP Label of the textfield for the username. - text: qsTr("Username") - } - GTextField { - id: userInput - - width: 500 * UiPluginModel.scaleFactor - } - GText { - //: LABEL DESKTOP Accessible name. - Accessible.name: qsTr("Proxy credential password") - - //: LABEL DESKTOP Label of the textfield for the password. - text: qsTr("Password") - } - GTextField { - id: passwordInput - - echoMode: TextInput.Password - width: 500 * UiPluginModel.scaleFactor - } - } -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/Subheading.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/Subheading.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/Subheading.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/Subheading.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick + +import Governikus.Style + +BaseHeading { + textStyle: Style.text.subline +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/TintableIcon.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/TintableIcon.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/TintableIcon.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/TintableIcon.qml 2025-10-30 10:10:48.000000000 +0000 @@ -6,6 +6,7 @@ import QtQuick import QtQuick.Controls +import QtQuick.Effects import Governikus.Style @@ -29,7 +30,7 @@ SequentialAnimation { PropertyAnimation { - duration: root.source == "" ? 0 : Style.animation_duration + duration: root.source.toString() === "" ? 0 : Style.animation_duration easing.type: Easing.InCubic property: "opacity" target: root @@ -40,8 +41,8 @@ target: root } PropertyAnimation { - duration: root.source == "" ? Style.animation_duration * 2 : Style.animation_duration - easing.type: root.source == "" ? Easing.InOutCubic : Easing.InCubic + duration: root.source.toString() === "" ? Style.animation_duration * 2 : Style.animation_duration + easing.type: root.source.toString() === "" ? Easing.InOutCubic : Easing.InCubic property: "opacity" target: root to: 1 @@ -75,14 +76,18 @@ Component { id: desaturateLayer - GDesaturate { + MultiEffect { + saturation: -1 } } Component { id: colorLayer - GColorOverlay { + MultiEffect { + brightness: 0.5 + colorization: 1 colorizationColor: root.tintColor + contrast: -1.0 } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/Utils.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/Utils.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/Utils.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/Utils.qml 2025-10-30 10:10:48.000000000 +0000 @@ -9,15 +9,71 @@ import Governikus.Type QtObject { - function positionViewAtItem(pItem, pParent = pItem.parent) { + function findGFlickable(pItem) { + if (pItem === null) { + return null; + } + if (pItem instanceof GFlickable) { + return pItem; + } + return findGFlickable(pItem.parent); + } + function platformAgnosticLinkOpenText(pLink, pName) { + if (Qt.platform.os === "ios" || Qt.platform.os === "android") { + //: INFO ANDROID IOS Hint that a link is present, which will open in the browser + return qsTr("Tap to open the following website in your browser: %1").arg(pLink); + } + //: INFO DESKTOP Hint that a link is present, which will open in the browser + let desktopDescription = qsTr("Press space to open the following website in your browser: %1").arg(pLink); + if (Qt.platform.os === "osx") { + return "%1, %2".arg(pName).arg(desktopDescription); + } + return desktopDescription; + } + function positionFlickableAtItem(pFlickable, pItem, pPositionItemAtMiddle = false) { + let castItem = (pItem as Item); + let referenceItem = castItem.parent ? (castItem.parent as Item) : castItem; + let mappedPosition = pFlickable.mapFromItem(referenceItem, castItem.x, castItem.y); + + if (pPositionItemAtMiddle) { + const absY = castItem.mapToItem(pFlickable.contentItem, 0, 0).y; + pFlickable.contentY = absY - Math.floor(pFlickable.height * 0.5); + return; + } + if (Qt.platform.os === "ios") { + UtilsIOS.handleFlickable(pFlickable, castItem); + return; + } + if (mappedPosition.y < 0) { + pFlickable.contentY = Math.max(0, pFlickable.contentY + mappedPosition.y); + return; + } + let viewDifference = mappedPosition.y + castItem.height - pFlickable.height; + if (viewDifference > 0) { + pFlickable.contentY = Math.min(pFlickable.contentY + viewDifference, pFlickable.contentHeight - pFlickable.height); + } + } + function positionViewAtItem(pItem, pParent = pItem.parent, pPositionItemAtMiddle = false) { if (pParent === null) { return; } - if (typeof pParent.positionViewAtItem === "function") { - pParent.positionViewAtItem(pItem); + if (pParent instanceof Flickable) { + positionFlickableAtItem(pParent, pItem, pPositionItemAtMiddle); return; } - positionViewAtItem(pItem, pParent.parent); + positionViewAtItem(pItem, pParent.parent, pPositionItemAtMiddle); + } + function scrollPageDownOnGFlickable(pItem) { + let gflickable = findGFlickable(pItem); + if (gflickable) { + gflickable.scrollPageDown(); + } + } + function scrollPageUpOnGFlickable(pItem) { + let gflickable = findGFlickable(pItem); + if (gflickable) { + gflickable.scrollPageUp(); + } } function shuffle(pArray) { for (let i = pArray.length - 1; i > 0; i--) { @@ -28,4 +84,10 @@ } return pArray; } + function useSpecialAppleTabRole(pRole) { + if ((Qt.platform.os === "osx" || Qt.platform.os === "ios") && !ApplicationModel.screenReaderRunning) + return Accessible.Link; + + return pRole; + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/UtilsIOS.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/UtilsIOS.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/UtilsIOS.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/UtilsIOS.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +pragma Singleton + +import QtQuick + +QtObject { + function findAllAccessibleItems(pItem) { + let accessibleItems = []; + + function traverse(currentItem) { + if (!currentItem || !currentItem.visible) { + return; + } + + const isA11yFocusable = currentItem.Accessible && currentItem.Accessible.focusable && !currentItem.Accessible.ignored; + if (isA11yFocusable) { + accessibleItems.push(currentItem); + } + + for (let i = 0; i < currentItem.children.length; i++) { + traverse(currentItem.children[i]); + } + } + + traverse(pItem); + return accessibleItems; + } + function findNextAccessibleItemInFlickable(pFlickable, pCurrentItem) { + const sortedItems = getSortedAccessibleItems(pFlickable); + const currentIndex = sortedItems.indexOf(pCurrentItem); + if (currentIndex !== -1 && currentIndex < sortedItems.length - 1) { + return sortedItems[currentIndex + 1]; + } + return null; + } + function findPreviousAccessibleItemInFlickable(pFlickable, pCurrentItem) { + const sortedItems = getSortedAccessibleItems(pFlickable); + const currentIndex = sortedItems.indexOf(pCurrentItem); + if (currentIndex > 0) { + return sortedItems[currentIndex - 1]; + } + return null; + } + function getSortedAccessibleItems(pFlickable) { + if (!pFlickable || !pFlickable.contentItem) { + return []; + } + const allItems = findAllAccessibleItems(pFlickable.contentItem); + return allItems.sort((a, b) => { + const yA = a.mapToItem(pFlickable.contentItem, 0, 0).y; + const yB = b.mapToItem(pFlickable.contentItem, 0, 0).y; + return yA - yB; + }); + } + function handleFlickable(pFlickable, pItem) { + let firstItemInGroup = findPreviousAccessibleItemInFlickable(pFlickable, pItem); + firstItemInGroup = firstItemInGroup !== null ? firstItemInGroup : pItem; + let lastItemInGroup = findNextAccessibleItemInFlickable(pFlickable, pItem); + lastItemInGroup = lastItemInGroup !== null ? lastItemInGroup : pItem; + + const groupTopY = firstItemInGroup.mapToItem(pFlickable.contentItem, 0, 0).y; + const groupBottomY = lastItemInGroup.mapToItem(pFlickable.contentItem, 0, 0).y + lastItemInGroup.height; + + const visibleTopY = pFlickable.contentY; + const visibleBottomY = pFlickable.contentY + pFlickable.height; + + let newContentY = pFlickable.contentY; + + if (groupTopY < visibleTopY) { + newContentY = groupTopY; + } else if (groupBottomY > visibleBottomY) { + newContentY = groupBottomY - pFlickable.height; + } + + if (pFlickable.contentHeight > pFlickable.height) { + pFlickable.contentY = Math.max(0, Math.min(newContentY, pFlickable.contentHeight - pFlickable.height)); + } + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/internal/BaseHeading.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/internal/BaseHeading.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/internal/BaseHeading.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/internal/BaseHeading.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick + +GText { + Accessible.name: { + if (Qt.platform.os !== "windows") { + return text; + } + //: LABEL WINDOWS Screenreader announcement, that the current item is a heading. + return text + " , " + qsTr("Heading"); + } + Accessible.role: Accessible.Heading +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Global/internal/CardNotActivatedBaseView.qml ausweisapp2-2.4.0/src/ui/qml/modules/Global/internal/CardNotActivatedBaseView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Global/internal/CardNotActivatedBaseView.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Global/internal/CardNotActivatedBaseView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts + +import Governikus.Animations +import Governikus.Global +import Governikus.ResultView +import Governikus.Type + +ResultView { + id: root + + property bool continueButtonVisible: false + //: LABEL ALL_PLATFORMS + readonly property string hasCodeSubHeaderText: qsTr("Activate ID card with PIN reset letter") + //: LABEL ALL_PLATFORMS + readonly property string hasNoCodeSubHeaderText: qsTr("Request eID function activation") + + signal decisionHasCodeClicked + signal decisionHasNoCodeClicked + + function pushSubView(hasCode, dict) { + root.push(hasCode ? hasCodeView : hasNoCodeView, dict); + } + + animationSymbol: Symbol.Type.BLOCK + animationType: AnimationLoader.Type.CARD_RESULT + buttonText: "" + //: LABEL ALL_PLATFORMS + header: qsTr("eID function is not activated") + //: LABEL ALL_PLATFORMS + subheader: PinResetInformationModel.hasPinResetService ? qsTr("Did you recently order a PIN reset letter with an activation code?") : hasNoCodeSubHeaderText + + Loader { + Layout.fillWidth: true + sourceComponent: PinResetInformationModel.hasPinResetService ? decisionViewContent : hasNoCodeContent + } + Component { + id: hasCodeView + + ResultView { + animationSymbol: root.animationSymbol + animationType: root.animationType + buttonText: "" + header: root.header + progress: root.progress + subheader: root.hasCodeSubHeaderText + title: root.title + + Loader { + Layout.fillWidth: true + sourceComponent: hasCodeContent + } + } + } + Component { + id: hasNoCodeView + + ResultView { + animationSymbol: root.animationSymbol + animationType: root.animationType + buttonText: "" + header: root.header + progress: root.progress + subheader: root.hasNoCodeSubHeaderText + title: root.title + + Loader { + Layout.fillWidth: true + sourceComponent: hasNoCodeContent + } + } + } + Component { + id: decisionViewContent + + ColumnLayout { + spacing: root.spacing + + GInformativeButton { + Layout.fillWidth: true + //: LABEL ALL_PLATFORMS + description: qsTr("Activate your ID card using the activation code") + isPane: true + //: LABEL ALL_PLATFORMS + text: qsTr("Yes, I already have an activation code") + + onClicked: root.decisionHasCodeClicked() + } + GInformativeButton { + Layout.fillWidth: true + //: LABEL ALL_PLATFORMS + description: qsTr("Request the activation of the eID function") + isPane: true + //: LABEL ALL_PLATFORMS + text: qsTr("No, I don't have an activation code") + + onClicked: root.decisionHasNoCodeClicked() + } + GContinueButton { + //: LABEL ALL_PLATFORMS + text: qsTr("Abort setup") + visible: root.continueButtonVisible + + onClicked: root.continueClicked() + } + } + } + Component { + id: hasCodeContent + + ColumnLayout { + spacing: root.spacing + + GText { + //: LABEL ALL_PLATFORMS + text: qsTr("Enter your activation code of your present PIN reset letter into the following website.") + } + GButton { + Accessible.description: Utils.platformAgnosticLinkOpenText(PinResetInformationModel.pinResetActivationUrl, Accessible.name) + Accessible.role: Accessible.Link + Layout.alignment: Qt.AlignHCenter + icon.source: "qrc:///images/open_website.svg" + //: LABEL ALL_PLATFORMS + text: qsTr("Enter activation code") + tintIcon: true + + onClicked: Qt.openUrlExternally(PinResetInformationModel.pinResetActivationUrl) + } + } + } + Component { + id: hasNoCodeContent + + ColumnLayout { + spacing: root.spacing + + Hint { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + //: LABEL ALL_PLATFORMS + buttonText: qsTr("Request action code") + linkToOpen: PinResetInformationModel.pinResetUrl + //: LABEL ALL_PLATFORMS + text: qsTr("You can request a PIN reset letter with an activation code on the following website.") + //: LABEL ALL_PLATFORMS + title: qsTr("Online via PIN reset service") + visible: PinResetInformationModel.hasPinResetService + } + Hint { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + //: LABEL ALL_PLATFORMS + buttonText: qsTr("Find competent authority") + linkToOpen: PinResetInformationModel.administrativeSearchUrl + //: LABEL ALL_PLATFORMS + text: qsTr("You can activate the eID function directly at your competent authority.") + //: LABEL ALL_PLATFORMS + title: qsTr("At your competent authority") + } + GContinueButton { + //: LABEL ALL_PLATFORMS + text: qsTr("Abort setup") + visible: root.continueButtonVisible && !PinResetInformationModel.hasPinResetService + + onClicked: root.continueClicked() + } + } + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/+desktop/DiagnosisView.qml ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/+desktop/DiagnosisView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/+desktop/DiagnosisView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/+desktop/DiagnosisView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -19,13 +19,13 @@ title: qsTr("System data") titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.leaveView() } Keys.onPressed: event => { - sectionContent.handleKeyPress(event.key); + sectionContent.handleKeyPress(event); } DiagnosisModel { @@ -107,7 +107,7 @@ //: LABEL DESKTOP title: qsTr("Save system data") - onAccepted: diagnosisModel.saveToFile(file) + onAccepted: diagnosisModel.saveToFile(selectedFile) } Timer { id: timeout diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/+desktop/LicenseInformation.qml ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/+desktop/LicenseInformation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/+desktop/LicenseInformation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/+desktop/LicenseInformation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,7 +14,13 @@ GListView { id: root - activeFocusOnTab: true + function isListElementEmptyFunc(pItem) { + let delegate = pItem as ListEntryDelegate; + if (delegate) + return delegate.text === ""; + return true; + } + anchors.fill: parent displayMarginBeginning: Style.dimens.pane_padding displayMarginEnd: Style.dimens.pane_padding @@ -22,6 +28,10 @@ delegate: ListEntryDelegate { z: 0 + + onActiveFocusChanged: if (activeFocus) { + root.handleItemFocused(index); + } } highlight: Item { z: 2 @@ -33,17 +43,6 @@ } } - Keys.onDownPressed: { - do { - root.incrementCurrentIndex(); - } while ((currentItem as ListEntryDelegate).text === "") - } - Keys.onUpPressed: { - do { - root.decrementCurrentIndex(); - } while ((currentItem as ListEntryDelegate).text === "") - } - layer { enabled: GraphicsInfo.api !== GraphicsInfo.Software @@ -51,39 +50,34 @@ } } - component ListEntryDelegate: RoundedRectangle { + component ListEntryDelegate: GPaneBackgroundDelegate { id: listEntryDelegate required property int index - readonly property bool isFirstItem: index === 0 - readonly property bool isLastItem: index === ListView.view.count - 1 required property string modelData readonly property alias text: delegateText.text + Accessible.focusable: true Accessible.ignored: delegateText.text === "" Accessible.name: delegateText.text - Accessible.role: Accessible.StaticText - bottomLeftCorner: isLastItem - bottomRightCorner: isLastItem - color: Style.color.pane.background.basic + Accessible.role: Utils.useSpecialAppleTabRole(Accessible.StaticText) + count: root.count + idx: index implicitHeight: Math.ceil(delegateText.implicitHeight) + delegateText.anchors.bottomMargin + delegateText.anchors.topMargin - topLeftCorner: isFirstItem - topRightCorner: isFirstItem width: root.width - Style.dimens.pane_padding GText { id: delegateText Accessible.ignored: true - activeFocusOnTab: false text: listEntryDelegate.modelData anchors { - bottomMargin: listEntryDelegate.isLastItem ? Style.dimens.pane_padding : 0 + bottomMargin: listEntryDelegate.isLast ? Style.dimens.pane_padding : 0 fill: parent leftMargin: Style.dimens.pane_padding rightMargin: Style.dimens.pane_padding - topMargin: listEntryDelegate.isFirstItem ? Style.dimens.pane_padding : Style.dimens.text_spacing + topMargin: listEntryDelegate.isFirst ? Style.dimens.pane_padding : Style.dimens.text_spacing } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/+desktop/VersionInformation.qml ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/+desktop/VersionInformation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/+desktop/VersionInformation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/+desktop/VersionInformation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -8,8 +8,8 @@ import QtQuick.Layouts import Governikus.Global -import Governikus.Type import Governikus.Style +import Governikus.Type ColumnLayout { id: root @@ -18,7 +18,6 @@ GPane { Layout.fillWidth: true - spacing: Style.dimens.pane_spacing GMenuItem { Layout.fillWidth: true @@ -27,11 +26,10 @@ buttonText: qsTr("Open website") buttonTooltip: "https://www.ausweisapp.bund.de/%1/aa2/privacy".arg(SettingsModel.language) iconSource: "qrc:/images/desktop/privacy_icon.svg" + linkToOpen: buttonTooltip //: LABEL DESKTOP title: qsTr("Privacy statement") - - onClicked: Qt.openUrlExternally(buttonTooltip) } GSeparator { Layout.fillWidth: true @@ -43,11 +41,10 @@ buttonText: qsTr("Open website") buttonTooltip: "https://www.ausweisapp.bund.de/%1/aa2/a11y".arg(SettingsModel.language) iconSource: "qrc:/images/desktop/a11y_icon.svg" + linkToOpen: buttonTooltip //: LABEL DESKTOP title: qsTr("Accessibility statement") - - onClicked: Qt.openUrlExternally(buttonTooltip) } } Item { @@ -60,7 +57,6 @@ id: column anchors.fill: parent - spacing: Style.dimens.pane_spacing Repeater { id: repeater @@ -75,12 +71,16 @@ text: value width: baseItem.width + Accessible.onPressAction: mouseArea.clicked(null) + Keys.onSpacePressed: mouseArea.clicked(null) onFocusChanged: if (focus) Utils.positionViewAtItem(this) } } } MouseArea { + id: mouseArea + property int counter: 0 anchors.fill: parent diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/+mobile/LicenseInformation.qml ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/+mobile/LicenseInformation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/+mobile/LicenseInformation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/+mobile/LicenseInformation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -33,7 +33,6 @@ GListView { id: listView - Accessible.ignored: false displayMarginBeginning: Style.dimens.pane_padding displayMarginEnd: Style.dimens.pane_padding model: ApplicationModel.getLicenseText() @@ -58,13 +57,16 @@ GText { id: delegateText - activeFocusOnTab: false + Accessible.role: Utils.useSpecialAppleTabRole(Accessible.StaticText) anchors.fill: parent bottomPadding: parent.isLast ? Style.dimens.pane_padding : 0 leftPadding: Style.dimens.pane_padding rightPadding: Style.dimens.pane_padding text: delegateItem.modelData topPadding: parent.isFirst ? Style.dimens.pane_padding : 0 + + Accessible.onScrollDownAction: listView.scrollPageDown() + Accessible.onScrollUpAction: listView.scrollPageUp() } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/+mobile/VersionInformation.qml ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/+mobile/VersionInformation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/+mobile/VersionInformation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/+mobile/VersionInformation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,8 +1,12 @@ /** * Copyright (c) 2016-2025 Governikus GmbH & Co. KG, Germany */ + +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Layouts + import Governikus.Global import Governikus.TitleBar import Governikus.View @@ -11,8 +15,8 @@ FlickableSectionPage { id: root - //: LABEL ANDROID IOS - title: qsTr("Version Information") + //: LABEL ANDROID IOS %1 is replaced with the application name + title: qsTr("%1 version").arg(Qt.application.name) navigationAction: NavigationAction { action: NavigationAction.Action.Back @@ -32,9 +36,9 @@ case 7: case 8: case 9: - if (!ApplicationModel.isScreenReaderRunning) { + if (!ApplicationModel.screenReaderRunning) { //: INFO ANDROID IOS Used in notifications when the user taps the version information - ApplicationModel.showFeedback(qsTr("%1 more presses to toggle the advanced settings.").arg(10 - advancedSettingsCounter), true); + ApplicationModel.showFeedback(qsTr("%1 more taps to toggle the advanced settings.").arg(10 - advancedSettingsCounter), true); } break; case 10: @@ -63,10 +67,12 @@ required property string key required property string value - anchors.left: parent.left - anchors.right: parent.right + Layout.fillWidth: true label: key text: value + + onFocusChanged: if (focus) + root.positionViewAtItem(this) } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/ReleaseNotesView.qml ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/ReleaseNotesView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/InformationView/ReleaseNotesView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/InformationView/ReleaseNotesView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -15,37 +15,39 @@ property real maximumContentWidth: Number.POSITIVE_INFINITY - Accessible.ignored: false - activeFocusOnTab: true + function isListElementEmptyFunc(pItem) { + let delegate = pItem as FormattedTextView; + if (delegate) + return delegate.content === ""; + return true; + } + displayMarginBeginning: Style.dimens.pane_padding displayMarginEnd: Style.dimens.pane_padding delegate: FormattedTextView { - maximumContentWidth: root.maximumContentWidth - totalItemCount: root.count - width: root.width + anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined + anchors.horizontalCenterOffset: -Style.dimens.pane_padding / 2 + count: root.count + width: Math.min(root.width - Style.dimens.pane_padding, root.maximumContentWidth) + + onActiveFocusChanged: if (activeFocus) { + root.handleItemFocused(index); + } + onScrollDownAction: root.scrollPageDown() + onScrollUpAction: root.scrollPageUp() } highlight: Item { z: 2 FocusFrame { anchors.leftMargin: 0 - anchors.rightMargin: Style.dimens.pane_padding + anchors.rightMargin: 0 scope: root + visible: Style.is_layout_desktop } } - Keys.onDownPressed: { - do { - root.incrementCurrentIndex(); - } while ((currentItem as FormattedTextView).content === "") - } - Keys.onUpPressed: { - do { - root.decrementCurrentIndex(); - } while ((currentItem as FormattedTextView).content === "") - } - layer { enabled: true diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Init/+desktop/App.qml ausweisapp2-2.4.0/src/ui/qml/modules/Init/+desktop/App.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Init/+desktop/App.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Init/+desktop/App.qml 2025-10-30 10:10:48.000000000 +0000 @@ -23,6 +23,7 @@ d.detachedLogView = detachedLogViewWindow.createObject(); } else { d.detachedLogView.raise(); + d.detachedLogView.requestActivate(); } } @@ -39,11 +40,26 @@ contentItem: (contentArea.currentItem as SectionPage) showPane: contentArea.depth > 1 + onShowUpdate: contentArea.setUiModule(UiModule.UPDATEINFORMATION) onStartClicked: contentArea.pop(null) } Component.onCompleted: titleBar.forceActiveFocus(Qt.MouseFocusReason) + onActiveChanged: { + if (root.active && contentArea.activeModule === UiModule.DEFAULT) { + switch (root.visibility) { + case ApplicationWindow.Windowed: + case ApplicationWindow.Maximized: + case ApplicationWindow.FullScreen: + UiPluginModel.showUpdateInformationIfPending(); + // fall through + default: + break; + } + } + } onClosing: close => { + ApplicationModel.fireAppAboutToQuit(); close.accepted = closeHandler.handle(); } onHeightChanged: d.setScaleFactor() @@ -112,7 +128,7 @@ ensureWidth(windowMargin); root.x = screenMinX + windowMargin; } - let screenMinY = root.Screen.virtualY; + let screenMinY = root.Screen.virtualY; // codespell:ignore if (root.y < screenMinY) { ensureHeight(windowMargin); root.y = screenMinY + windowMargin; @@ -180,7 +196,6 @@ readonly property var currentSectionPage: contentArea.currentItem - activeFocusOnTab: ApplicationModel.isScreenReaderRunning progress: currentSectionPage ? currentSectionPage.progress : null visible: progress !== null && progress.enabled @@ -206,8 +221,18 @@ root.show(); root.raise(); } - onHide: d.hideUiAndTaskbarEntry() - onQuit: UiPluginModel.fireQuitApplicationRequest() + onHide: { + if (redirectOnSuccess) { + AuthModel.continueWorkflow(); + } + d.hideUiAndTaskbarEntry(); + } + onQuit: { + if (redirectOnSuccess) { + AuthModel.continueWorkflow(); + } + UiPluginModel.fireQuitApplicationRequest(); + } onShowMinimized: root.showMinimized() } Connections { @@ -230,14 +255,41 @@ d.showMainWindow(); closeHandler.closeOpenDialogs(); d.showDetachedLogViewIfPresent(); - contentArea.setUiModule(pModule); d.ensureScreenFit(); + + switch (ApplicationModel.currentWorkflow) { + case ApplicationModel.Workflow.CHANGE_PIN: + if (pModule !== UiModule.PINMANAGEMENT) { + return; + } + break; + case ApplicationModel.Workflow.SELF_AUTHENTICATION: + case ApplicationModel.Workflow.AUTHENTICATION: + if (pModule !== UiModule.IDENTIFY) { + return; + } + break; + case ApplicationModel.Workflow.REMOTE_SERVICE: + case ApplicationModel.Workflow.SMART: + case ApplicationModel.Workflow.NONE: + break; + } + contentArea.setUiModule(pModule); + } + function onIsUpdatePendingChanged() { + if (!UiPluginModel.isUpdatePending) { + return; + } + + if (root.active && contentArea.activeModule === UiModule.DEFAULT) { + UiPluginModel.showUpdateInformationIfPending(); + } } target: UiPluginModel } Connections { - function onFireAppUpdateDataChanged() { + function onFireAppUpdateDataChanged(pAfterManualRequest) { if (!SettingsModel.appUpdateData.valid) { //: INFO DESKTOP Message that the update data is invalid and can't be used. ApplicationModel.showFeedback(qsTr("Failed to retrieve update information.")); diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Init/+desktop/internal/CloseHandler.qml ausweisapp2-2.4.0/src/ui/qml/modules/Init/+desktop/internal/CloseHandler.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Init/+desktop/internal/CloseHandler.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Init/+desktop/internal/CloseHandler.qml 2025-10-30 10:10:48.000000000 +0000 @@ -4,46 +4,36 @@ import QtQuick import QtQuick.Controls -import QtQuick.Layouts import Governikus.Global -import Governikus.Style import Governikus.Type Item { id: root + //: LABEL DESKTOP + readonly property string buttonCloseText: qsTr("Close completely") + //: LABEL DESKTOP + readonly property string buttonLeaveActiveText: qsTr("Leave active in the background") //: INFO DESKTOP %1 is replaced with the application name readonly property string closeWarningText: qsTr("If the %1 is closed, it is no longer available for authentication. You must then restart the app to authenticate yourself to service providers.").arg(Qt.application.name) + //: INFO DESKTOP Header of the popup that is shown when the AA is closed for the first time. + readonly property string closeWarningTitle: qsTr("How should the %1 be closed in the future?").arg(Qt.application.name) property var closingPopup: SettingsModel.trayIconEnabled ? closeWithEnabledTrayIconWarning : closeWarningDisabledTrayIcon readonly property string dontHideToTrayText: { - if (Qt.platform.os === "osx") { - return closeWarningText + "

" + - //: INFO DESKTOP Content of the MacOS-popup that is shown when the AA is closed and the tray icon is disabled. - qsTr("If you only close the user interface, the app remains active in the background in the future and can be opened again via the %1 icon on the menu bar.").arg(Qt.application.name); - } else if (Qt.platform.os === "windows") { - //: INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is disabled. - return qsTr("If the %1 is terminated, it is no longer available for authentication. You must then restart the app in order to identify yourself to service providers.").arg(Qt.application.name) + "

" + - //: INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is disabled. - qsTr("If you only close the user interface, the app remains active in the background in the future and can be reopened via the %1 icon in the notification area of the Windows taskbar.").arg(Qt.application.name); - } - - //: INFO DESKTOP Content of the popup that is shown when the AA is closed and the tray icon is disabled. - return root.closeWarningText; + return closeWarningText + "

" + remainActiveWarningText + "

" + settingsNoteText; } property bool hasOpenSubwindows: false readonly property string hideToTrayText: { - if (Qt.platform.os === "osx") { - //: INFO DESKTOP Content of the MacOS-popup that is shown when the AA is closed and the tray icon is enabled. - return qsTr("The app remains active in the background and can be reopened via the %1 icon on the menu bar again.").arg(Qt.application.name) + "

" + closeWarningText; - } else if (Qt.platform.os === "windows") { - //: INFO DESKTOP Content of the Windows-popup that is shown when the AA is closed and the tray icon is enabled. - return qsTr("The app remains active in the background and can be reopened via the %1 icon in the notification area of the Windows taskbar.").arg(Qt.application.name) + "

" + closeWarningText; - } - - //: INFO DESKTOP Content of the popup that is shown when the AA is closed and the tray icon is enabled. - return qsTr("The app remains available via the icon in the system tray. Click on the %1 icon to reopen the user interface.").arg(Qt.application.name); + return remainActiveWarningText + "

" + closeWarningText + "

" + settingsNoteText; } + readonly property bool redirectOnSuccess: AuthModel.statusCode === GlobalStatusCode.No_Error && AuthModel.currentState === "StateRedirectBrowser" + //: INFO DESKTOP %1 is replaced with the application name + readonly property string remainActiveWarningText: qsTr("If the %1 remains active in the background, it will open automatically as soon as you start an authentication. You can still open the %1 manually at any time.").arg(Qt.application.name) + //: INFO DESKTOP Note to the user that the setting is available in the settings + readonly property string settingsNoteText: qsTr("You can change your selection at any time in the settings.") + //: LABEL DESKTOP + readonly property string settingsReminderText: qsTr("Do not display this message in future.") signal abortWorkflow signal bringToFront @@ -64,6 +54,9 @@ quit(); } function handle() { + if (redirectOnSuccess) { + return showPopupOrCloseHide(); + } if (ApplicationModel.currentWorkflow !== ApplicationModel.Workflow.NONE) { abortWorkflowWarning.open(); bringToFront(); @@ -73,13 +66,7 @@ showMinimized(); return false; } - if (SettingsModel.remindUserToClose) { - closingPopup.open(); - bringToFront(); - return false; - } - closeOrHide(); - return true; + return showPopupOrCloseHide(); } function handleFullClose() { SettingsModel.trayIconEnabled = false; @@ -89,6 +76,15 @@ SettingsModel.trayIconEnabled = true; root.closeOrHide(); } + function showPopupOrCloseHide() { + if (SettingsModel.remindUserToClose) { + closingPopup.open(); + bringToFront(); + return false; + } + closeOrHide(); + return true; + } ConfirmationPopup { id: abortWorkflowWarning @@ -103,6 +99,8 @@ } closePolicy: Popup.NoAutoClose + //: INFO DESKTOP + okButtonText: qsTr("Abort process") text: abortText //: INFO DESKTOP Header of the popup that is shown when the AA is closed and a workflow is still active title: qsTr("Abort operation") @@ -116,77 +114,44 @@ close(); } } - BaseConfirmationPopup { + ConfirmationPopup { id: closeWithEnabledTrayIconWarning + cancelButtonText: root.buttonCloseText closePolicy: Popup.NoAutoClose + okButtonText: root.buttonLeaveActiveText showCloseButton: true text: root.hideToTrayText - //: INFO DESKTOP Header of the popup that is shown when the AA is closed for the first time. - title: qsTr("The user interface of the %1 is closed.").arg(Qt.application.name) - - buttons: RowLayout { - layoutDirection: Qt.RightToLeft - spacing: Style.dimens.pane_spacing - width: parent.width - - GButton { - style: Style.color.controlOptional - //: LABEL DESKTOP - text: qsTr("Completely close the app") - - onClicked: root.handleFullClose() - } - GButton { - //: LABEL DESKTOP - text: qsTr("Just close the user interface") + title: root.closeWarningTitle - onClicked: root.handleJustCloseUi() - } - } + onCancelled: root.handleFullClose() + onConfirmed: root.handleJustCloseUi() GCheckBox { checked: !SettingsModel.remindUserToClose - //: LABEL DESKTOP - text: qsTr("Do not display this message in future.") + horizontalPadding: 0 + text: root.settingsReminderText onCheckedChanged: SettingsModel.remindUserToClose = !checked } } - BaseConfirmationPopup { + ConfirmationPopup { id: closeWarningDisabledTrayIcon + cancelButtonText: root.buttonLeaveActiveText closePolicy: Popup.NoAutoClose + okButtonText: root.buttonCloseText showCloseButton: true - style: ConfirmationPopup.PopupStyle.OkButton text: root.dontHideToTrayText - //: INFO DESKTOP Header of the popup that is shown when the AA is quit for the first time. - title: qsTr("The %1 is closed.").arg(Qt.application.name) - - buttons: RowLayout { - layoutDirection: Qt.RightToLeft - spacing: Style.dimens.pane_spacing - width: parent.width - - GButton { - style: Style.color.controlOptional - //: LABEL DESKTOP - text: Qt.platform.os === "osx" ? qsTr("Close user interface to menu bar") : qsTr("Just close the user interface") + title: root.closeWarningTitle - onClicked: root.handleJustCloseUi() - } - GButton { - //: LABEL DESKTOP - text: qsTr("Completely close the app") - - onClicked: root.handleFullClose() - } - } + onCancelled: root.handleJustCloseUi() + onConfirmed: root.handleFullClose() GCheckBox { checked: !SettingsModel.remindUserToClose - //: LABEL DESKTOP - text: qsTr("Do not display this message in future.") + horizontalPadding: 0 + text: root.settingsReminderText onCheckedChanged: SettingsModel.remindUserToClose = !checked } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Init/+mobile/App.qml ausweisapp2-2.4.0/src/ui/qml/modules/Init/+mobile/App.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Init/+mobile/App.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Init/+mobile/App.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,8 +1,10 @@ /** * Copyright (c) 2015-2025 Governikus GmbH & Co. KG, Germany */ + import QtQuick import QtQuick.Controls + import Governikus.Global import Governikus.TitleBar import Governikus.Navigation @@ -17,7 +19,23 @@ property var feedbackPopup: null readonly property Navigation navigationInstance: navigation - flags: Qt.platform.os === "ios" ? Qt.Window | Qt.MaximizeUsingFullscreenGeometryHint : Qt.Window + function closeFeedbackPopup() { + if (feedbackPopup) { + feedbackPopup.close(); + feedbackPopup.destroy(); + feedbackPopup = null; + } + } + function closeOpenPopups() { + closeFeedbackPopup(); + feedback.close(); + } + + bottomPadding: footer.height + flags: Qt.platform.os === "ios" ? Qt.Window | Qt.ExpandedClientAreaHint : Qt.Window + leftPadding: 0 + rightPadding: 0 + topPadding: menuBar.height visible: true background: Rectangle { @@ -60,7 +78,7 @@ // back button pressed pClose.accepted = false; if (contentArea.visibleItem) { - if (contentArea.activeModule === UiModule.DEFAULT) { + if (contentArea.activeModule === UiModule.DEFAULT && Style.is_layout_android) { let currentTime = new Date(); if (currentTime - d.lastCloseInvocation < 1000) { UiPluginModel.fireQuitApplicationRequest(); @@ -69,7 +87,7 @@ } 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, press the back button 2 times.")); + ApplicationModel.showFeedback(qsTr("To close the app, tap the back button 2 times.")); return; } let activeStackView = contentArea.visibleItem; @@ -102,6 +120,28 @@ } Connections { function onFireShowRequest(pModule) { + switch (ApplicationModel.currentWorkflow) { + case ApplicationModel.Workflow.CHANGE_PIN: + if (pModule !== UiModule.PINMANAGEMENT) { + return; + } + break; + case ApplicationModel.Workflow.SELF_AUTHENTICATION: + case ApplicationModel.Workflow.AUTHENTICATION: + if (pModule !== UiModule.IDENTIFY) { + return; + } + break; + case ApplicationModel.Workflow.REMOTE_SERVICE: + if (pModule !== UiModule.REMOTE_SERVICE) { + return; + } + break; + case ApplicationModel.Workflow.SMART: + case ApplicationModel.Workflow.NONE: + break; + } + switch (pModule) { case UiModule.CURRENT: break; @@ -112,10 +152,12 @@ navigation.show(UiModule.HELP); break; case UiModule.IDENTIFY: + root.closeOpenPopups(); if (ApplicationModel.currentWorkflow === ApplicationModel.Workflow.NONE) { navigation.show(UiModule.SELF_AUTHENTICATION); break; } + // fall through default: navigation.show(pModule); break; @@ -164,12 +206,11 @@ onBackGestureTriggered: titleBar.navigationAction.clicked() } Connections { + function onFireA11yFocusChanged(pItem) { + Utils.positionViewAtItem(pItem); + } function onFireFeedbackChanged() { - if (root.feedbackPopup) { - root.feedbackPopup.close(); - root.feedbackPopup.destroy(); - root.feedbackPopup = null; - } + root.closeFeedbackPopup(); if (ApplicationModel.feedback !== "") { root.feedbackPopup = toast.createObject(root, { text: ApplicationModel.feedback @@ -184,10 +225,10 @@ id: toast ConfirmationPopup { - closePolicy: ApplicationModel.isScreenReaderRunning ? Popup.NoAutoClose : Popup.CloseOnReleaseOutside + closePolicy: ApplicationModel.screenReaderRunning ? Popup.NoAutoClose : Popup.CloseOnReleaseOutside dim: true - modal: ApplicationModel.isScreenReaderRunning - style: ApplicationModel.isScreenReaderRunning ? ConfirmationPopup.PopupStyle.OkButton : ConfirmationPopup.PopupStyle.NoButtons + modal: ApplicationModel.screenReaderRunning + style: ApplicationModel.screenReaderRunning ? ConfirmationPopup.PopupStyle.OkButton : ConfirmationPopup.PopupStyle.NoButtons onConfirmed: ApplicationModel.onShowNextFeedback() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MainView/+desktop/internal/Tile.qml ausweisapp2-2.4.0/src/ui/qml/modules/MainView/+desktop/internal/Tile.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MainView/+desktop/internal/Tile.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MainView/+desktop/internal/Tile.qml 2025-10-30 10:10:48.000000000 +0000 @@ -2,14 +2,13 @@ * Copyright (c) 2018-2025 Governikus GmbH & Co. KG, Germany */ import QtQuick -import QtQuick.Controls import QtQuick.Layouts import Governikus.Global import Governikus.Style import Governikus.View import Governikus.Type -AbstractButton { +GAbstractButton { id: root Accessible.name: ApplicationModel.stripHtmlTags(text) @@ -21,8 +20,6 @@ padding: 2 * Style.dimens.pane_padding background: GPaneBackground { - id: pane - border.color: colors.paneBorder color: colors.paneBackground opacity: SettingsModel.showBetaTesting ? 0.9 : 1.0 @@ -37,16 +34,12 @@ spacing: Style.dimens.pane_spacing TintableIcon { - id: image - source: root.icon.source sourceSize.height: Style.dimens.huge_icon_size tintColor: Style.color.textTitle.checked } GText { - id: label - - activeFocusOnTab: false + Accessible.ignored: true color: root.activeFocus && UiPluginModel.showFocusIndicator ? Style.color.textTitle.checked : Style.color.textTitle.basic horizontalAlignment: Text.AlignLeft text: root.text diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MainView/+mobile/MainView.qml ausweisapp2-2.4.0/src/ui/qml/modules/MainView/+mobile/MainView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MainView/+mobile/MainView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MainView/+mobile/MainView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -33,10 +33,8 @@ required property int module required property string titleText - Accessible.ignored: tileView.allItemsVisible ? false : index !== tileView.currentIndex Accessible.name: titleText + ". " + qsTr("Item %1 of %2").arg(index + 1).arg(tileView.count) + (tileView.allItemsVisible ? "" : " . " + tileView.scrollHint) - activeFocusOnTab: false - focusPolicy: Qt.TabFocus + focusPolicy: ApplicationModel.screenReaderRunning ? Qt.StrongFocus : Qt.TabFocus height: ListView.view.height image: imagePath title: titleText @@ -46,12 +44,11 @@ tileView.decrementCurrentIndex() Accessible.onScrollRightAction: if (tileView.isIos) tileView.incrementCurrentIndex() - Component.onCompleted: tileDelegate.DelegateModel.inItems = module !== UiModule.SMART_EID || ApplicationModel.isSmartSupported + Component.onCompleted: tileDelegate.DelegateModel.inItems = module !== UiModule.SMART_EID || ApplicationModel.smartSupported onClicked: root.show(module) + onFocusChanged: if (focus && (!tileView.moving || ApplicationModel.screenReaderRunning)) + tileView.positionViewAtIndex(index, ListView.SnapPosition) - PointHandler { - onGrabChanged: tileView.focus = false - } FocusFrame { marginFactor: -2 } @@ -124,10 +121,8 @@ Layout.maximumWidth: allItemsVisible ? allItemsWidth : Number.POSITIVE_INFINITY Layout.minimumHeight: minItemHeight Layout.minimumWidth: Math.ceil(minItemWidth / overlapFactor) - activeFocusOnTab: true boundsBehavior: Flickable.DragAndOvershootBounds cacheBuffer: Number.POSITIVE_INFINITY - clip: true highlightMoveDuration: 250 highlightRangeMode: allItemsVisible ? ListView.NoHighlightRange : ListView.StrictlyEnforceRange interactive: !allItemsVisible || UiPluginModel.isChromeOS @@ -136,6 +131,7 @@ orientation: Qt.Horizontal preferredHighlightBegin: width / 2 - itemWidth / 2 preferredHighlightEnd: width / 2 + itemWidth / 2 + reuseItems: true snapMode: ListView.SnapOneItem onCountChanged: { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MainView/+mobile/internal/Tile.qml ausweisapp2-2.4.0/src/ui/qml/modules/MainView/+mobile/internal/Tile.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MainView/+mobile/internal/Tile.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MainView/+mobile/internal/Tile.qml 2025-10-30 10:10:48.000000000 +0000 @@ -2,12 +2,11 @@ * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany */ import QtQuick -import QtQuick.Controls import QtQuick.Layouts import Governikus.Global import Governikus.Style -AbstractButton { +GAbstractButton { id: root readonly property bool flowVertically: height > topPadding + image.implicitHeight + layout.spacing + text.effectiveMaxLinesHeight + bottomPadding @@ -61,7 +60,6 @@ id: text Accessible.ignored: true - activeFocusOnTab: false elide: Text.ElideRight horizontalAlignment: Text.AlignLeft maximumLineCount: 3 diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MoreView/+desktop/MoreView.qml ausweisapp2-2.4.0/src/ui/qml/modules/MoreView/+desktop/MoreView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MoreView/+desktop/MoreView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MoreView/+desktop/MoreView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -25,13 +25,13 @@ title: qsTr("Help") titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } Keys.onPressed: event => { - tabbedPane.handleKeyPress(event.key); + tabbedPane.handleKeyPress(event); } TabbedPane { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MoreView/+desktop/internal/MoreViewDiagnosis.qml ausweisapp2-2.4.0/src/ui/qml/modules/MoreView/+desktop/internal/MoreViewDiagnosis.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MoreView/+desktop/internal/MoreViewDiagnosis.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MoreView/+desktop/internal/MoreViewDiagnosis.qml 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,6 @@ import QtQuick.Layouts import Governikus.Global -import Governikus.Style GPane { id: root @@ -13,8 +12,6 @@ signal showDiagnosis signal showLogs - spacing: Style.dimens.pane_spacing - GMenuItem { Layout.fillWidth: true //: LABEL DESKTOP diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MoreView/+desktop/internal/MoreViewGeneral.qml ausweisapp2-2.4.0/src/ui/qml/modules/MoreView/+desktop/internal/MoreViewGeneral.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MoreView/+desktop/internal/MoreViewGeneral.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MoreView/+desktop/internal/MoreViewGeneral.qml 2025-10-30 10:10:48.000000000 +0000 @@ -7,23 +7,20 @@ import Governikus.Global import Governikus.Type -import Governikus.Style GPane { id: root signal startOnboarding - spacing: Style.dimens.pane_spacing - GMenuItem { Layout.fillWidth: true //: LABEL DESKTOP - buttonText: qsTr("Start onboarding") + buttonText: qsTr("Start setup") iconSource: "qrc:/images/npa.svg" tintIcon: false //: LABEL DESKTOP - title: qsTr("Onboarding") + title: qsTr("Setup") onClicked: root.startOnboarding() } @@ -37,11 +34,10 @@ buttonText: qsTr("Open website") buttonTooltip: "https://www.ausweisapp.bund.de/%1/aa2/faq".arg(SettingsModel.language) iconSource: "qrc:/images/faq_icon.svg" + linkToOpen: buttonTooltip //: LABEL DESKTOP title: qsTr("FAQ - Frequently asked questions") - - onClicked: Qt.openUrlExternally(buttonTooltip) } GSeparator { Layout.fillWidth: true @@ -53,11 +49,10 @@ buttonText: qsTr("Open website") buttonTooltip: "https://www.ausweisapp.bund.de/%1/aa2/support".arg(SettingsModel.language) iconSource: "qrc:/images/desktop/help_icon.svg" + linkToOpen: buttonTooltip //: LABEL DESKTOP title: qsTr("Contact") - - onClicked: Qt.openUrlExternally(buttonTooltip) } GSeparator { Layout.fillWidth: true @@ -69,10 +64,9 @@ buttonText: qsTr("Open website") buttonTooltip: "https://www.ausweisapp.bund.de/%1/aa2/providerlist".arg(SettingsModel.language) iconSource: "qrc:/images/identify.svg" + linkToOpen: buttonTooltip //: LABEL DESKTOP title: qsTr("List of Providers") - - onClicked: Qt.openUrlExternally(buttonTooltip) } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MoreView/+mobile/MoreView.qml ausweisapp2-2.4.0/src/ui/qml/modules/MoreView/+mobile/MoreView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MoreView/+mobile/MoreView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MoreView/+mobile/MoreView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -33,99 +33,69 @@ width: parent.width GMenuItem { + Layout.fillWidth: true drawTopCorners: true icon.source: "qrc:///images/npa.svg" tintIcon: false //: LABEL ANDROID IOS - title: qsTr("Start onboarding") - width: parent.width + title: qsTr("Start setup") onClicked: root.startOnboarding() } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + MoreViewSeparator { } GMenuItem { + Layout.fillWidth: true icon.source: "qrc:///images/open_website.svg" + linkToOpen: "https://www.ausweisapp.bund.de/%1/aa2/faq".arg(SettingsModel.language) //: LABEL ANDROID IOS title: qsTr("FAQ - Frequently asked questions") - width: parent.width - - onClicked: Qt.openUrlExternally("https://www.ausweisapp.bund.de/%1/aa2/faq".arg(SettingsModel.language)) } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + MoreViewSeparator { } GMenuItem { + Layout.fillWidth: true icon.source: "qrc:///images/open_website.svg" + linkToOpen: "https://www.ausweisapp.bund.de/%1/aa2/support".arg(SettingsModel.language) //: LABEL ANDROID IOS title: qsTr("Contact") - width: parent.width - - onClicked: Qt.openUrlExternally("https://www.ausweisapp.bund.de/%1/aa2/support".arg(SettingsModel.language)) } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + MoreViewSeparator { } GMenuItem { + Layout.fillWidth: true icon.source: "qrc:///images/open_website.svg" + linkToOpen: "https://www.ausweisapp.bund.de/%1/aa2/privacy".arg(SettingsModel.language) //: LABEL ANDROID IOS title: qsTr("Privacy statement") - width: parent.width - - onClicked: Qt.openUrlExternally("https://www.ausweisapp.bund.de/%1/aa2/privacy".arg(SettingsModel.language)) } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + MoreViewSeparator { } GMenuItem { + Layout.fillWidth: true icon.source: "qrc:///images/open_website.svg" + linkToOpen: "https://www.ausweisapp.bund.de/%1/aa2/a11y".arg(SettingsModel.language) //: LABEL ANDROID IOS title: qsTr("Accessibility statement") - width: parent.width - - onClicked: Qt.openUrlExternally("https://www.ausweisapp.bund.de/%1/aa2/a11y".arg(SettingsModel.language)) } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + MoreViewSeparator { } GMenuItem { + Layout.fillWidth: true icon.source: "qrc:///images/open_website.svg" + linkToOpen: "https://www.ausweisapp.bund.de/%1/aa2/providerlist".arg(SettingsModel.language) //: LABEL ANDROID IOS title: qsTr("List of Providers") - width: parent.width - - onClicked: Qt.openUrlExternally("https://www.ausweisapp.bund.de/%1/aa2/providerlist".arg(SettingsModel.language)) } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + MoreViewSeparator { } GMenuItem { + Layout.fillWidth: true drawBottomCorners: true icon.source: "qrc:///images/open_website.svg" + linkToOpen: ApplicationModel.storeUrl //: LABEL ANDROID IOS title: qsTr("Rate %1").arg(Qt.application.name) - width: parent.width - - onClicked: Qt.openUrlExternally(ApplicationModel.storeUrl) } } GOptionsContainer { @@ -134,35 +104,55 @@ width: parent.width GMenuItem { + id: menuItemShowLogs + + Layout.fillWidth: true drawTopCorners: true //: LABEL ANDROID IOS title: qsTr("Show Logs") - width: parent.width - onClicked: root.push(logPage) + onClicked: LogFilesModel.count > 1 ? root.push(logFilesView) : root.push(logView, { + logFileName: LogFilesModel.getLogFileName(0), + logFilePath: LogFilesModel.getLogFilePath(0) + }) Component { - id: logPage + id: logFilesView + + LogFilesView { + enableTileStyle: root.enableTileStyle + + onLogFilesListItemClicked: index => { + root.push(logView, { + logFileName: LogFilesModel.getLogFileName(index), + logFilePath: LogFilesModel.getLogFilePath(index) + }); + } + } + } + Component { + id: logView LogView { enableTileStyle: root.enableTileStyle } } } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + MoreViewSeparator { } GMenuItem { + Layout.fillWidth: true drawBottomCorners: true icon.source: "qrc:///images/email_icon.svg" //: LABEL ANDROID IOS title: qsTr("Send log to the support") - width: parent.width - onClicked: LogModel.mailLog() + onClicked: logModel.mailLogFile() + + LogModel { + id: logModel + + } } } GOptionsContainer { @@ -171,10 +161,10 @@ width: parent.width GMenuItem { + Layout.fillWidth: true drawTopCorners: true - //: LABEL ANDROID IOS - title: qsTr("Version information") - width: parent.width + //: LABEL ANDROID IOS %1 is replaced with the application name + title: qsTr("%1 version").arg(Qt.application.name) onClicked: root.push(versionInformation) @@ -186,16 +176,12 @@ } } } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + MoreViewSeparator { } GMenuItem { + Layout.fillWidth: true //: LABEL ANDROID IOS title: qsTr("Terms of use and software license") - width: parent.width onClicked: root.push(licenseInformation) @@ -207,17 +193,13 @@ } } } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + MoreViewSeparator { } GMenuItem { + Layout.fillWidth: true drawBottomCorners: true //: LABEL ANDROID IOS title: qsTr("Release notes") - width: parent.width onClicked: root.push(releaseNotes) @@ -231,4 +213,10 @@ } } } + + component MoreViewSeparator: GSeparator { + Layout.fillWidth: true + Layout.leftMargin: Style.dimens.pane_spacing + Layout.rightMargin: Layout.leftMargin + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MultiInfoView/+desktop/MultiInfoView.qml ausweisapp2-2.4.0/src/ui/qml/modules/MultiInfoView/+desktop/MultiInfoView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MultiInfoView/+desktop/MultiInfoView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MultiInfoView/+desktop/MultiInfoView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -27,21 +27,9 @@ signal abortCurrentWorkflow signal continueClicked - function continueOrLeave() { - if (continueButton.visible) { - root.continueClicked(); - } else { - root.leaveView(); - } - } - spacing: Style.dimens.pane_spacing title: infoContent.title - Keys.onEnterPressed: root.continueOrLeave() - Keys.onEscapePressed: root.leaveView() - Keys.onReturnPressed: root.continueOrLeave() - GRepeater { id: repeater @@ -53,7 +41,7 @@ required property MultiInfoContentBlock modelData - Layout.alignment: modelData.blockAligment + Layout.alignment: modelData.blockAlignment spacing: 2 * Style.dimens.pane_spacing AnimationLoader { @@ -69,17 +57,16 @@ source: dataRow.modelData.blockImage sourceSize.width: Math.max(repeater.maxItemWidth, Style.dimens.header_icon_size) tintColor: Style.color.image - visible: source != "" + visible: source.toString() !== "" } ColumnLayout { Layout.alignment: Qt.AlignTop spacing: Style.dimens.groupbox_spacing - GText { + Subheading { Layout.alignment: Qt.AlignLeft horizontalAlignment: Text.AlignLeft text: dataRow.modelData.blockTitle - textStyle: Style.text.subline visible: text !== "" } Repeater { @@ -88,6 +75,7 @@ delegate: GText { required property string modelData + Accessible.role: (textStyle === Style.text.headline || textStyle === Style.text.subline) ? Accessible.Heading : Accessible.StaticText Layout.alignment: Qt.AlignLeft horizontalAlignment: Text.AlignLeft text: modelData @@ -106,15 +94,13 @@ Layout.fillWidth: true buttonText: root.hintButtonText buttonTooltip: root.buttonLink + linkToOpen: root.buttonLink text: root.hint //: LABEL DESKTOP title: root.hintTitle !== "" ? root.hintTitle : qsTr("Hint") visible: text !== "" - onClicked: { - root.abortCurrentWorkflow(); - Qt.openUrlExternally(root.buttonLink); - } + onLinkAboutToOpen: root.abortCurrentWorkflow() } GSpacer { Layout.fillHeight: true diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MultiInfoView/+mobile/MultiInfoView.qml ausweisapp2-2.4.0/src/ui/qml/modules/MultiInfoView/+mobile/MultiInfoView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MultiInfoView/+mobile/MultiInfoView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MultiInfoView/+mobile/MultiInfoView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -66,9 +66,8 @@ spacing: Style.dimens.groupbox_spacing width: parent.width - GText { + Subheading { text: blockDelegate.blockTitle - textStyle: Style.text.subline visible: text !== "" width: parent.width wrapMode: Text.WordWrap @@ -80,6 +79,7 @@ delegate: GText { required property string modelData + Accessible.role: (textStyle === Style.text.headline || textStyle === Style.text.subline) ? Accessible.Heading : Accessible.StaticText text: modelData textStyle: blockDelegate.paragraphTextStyle visible: text !== "" @@ -109,15 +109,13 @@ Layout.fillWidth: true Layout.topMargin: Style.dimens.pane_spacing buttonText: root.hintButtonText + linkToOpen: root.buttonLink text: root.hint //: LABEL ANDROID IOS title: root.hintTitle !== "" ? root.hintTitle : qsTr("Hint") visible: text !== "" - onClicked: { - root.abortCurrentWorkflow(); - Qt.openUrlExternally(root.buttonLink); - } + onLinkAboutToOpen: root.abortCurrentWorkflow() } GSpacer { Layout.fillHeight: true diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MultiInfoView/MultiInfoData.qml ausweisapp2-2.4.0/src/ui/qml/modules/MultiInfoView/MultiInfoData.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MultiInfoView/MultiInfoData.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MultiInfoView/MultiInfoData.qml 2025-10-30 10:10:48.000000000 +0000 @@ -51,7 +51,7 @@ contentList: [ MultiInfoContentBlock { - blockHeaderAnimation: AnimationLoader.PIN + blockHeaderAnimation: AnimationLoader.Type.PIN //: LABEL ALL_PLATFORMS blockTitle: qsTr("What is the card PIN?") paragraphList: [ @@ -62,7 +62,7 @@ //: LABEL ALL_PLATFORMS blockTitle: qsTr("Where can I find the card PIN?") paragraphList: { - if (ApplicationModel.isSmartSupported) { + if (ApplicationModel.smartSupported) { //: INFO ALL_PLATFORMS Answer to the question 'Where can I find the card PIN?' (%1 is replaced with the application name) return [qsTr("You set the card PIN either directly when you picked up your ID card at the citizens' office (Bürgeramt) or later in %1 using the 5-digit Transport PIN. Only when you have set a 6-digit PIN of your own choice can you use the eID function and set up a Smart-eID.").arg(Qt.application.name)]; } @@ -82,7 +82,7 @@ blockTitle: qsTr("I just have a 5-digit Transport PIN") paragraphList: [ //: INFO ALL_PLATFORMS Explanation if only the Transport PIN is at hand - qsTr("You need to change the %1 5-digit Transport PIN%2 to your personal card PIN. Use %1Change PIN > Transport PIN%2 from the startpage to do so.").arg("").arg("")] + qsTr("You need to change the%1 5-digit Transport PIN%2 to your personal card PIN. Use %1Change PIN > Transport PIN%2 from the startpage to do so.").arg("").arg("")] } ] }, @@ -125,7 +125,7 @@ contentList: [ MultiInfoContentBlock { - blockHeaderAnimation: AnimationLoader.NEW_PIN + blockHeaderAnimation: AnimationLoader.Type.NEW_PIN //: LABEL ALL_PLATFORMS blockTitle: qsTr("How do I choose a secure PIN?") paragraphList: [ @@ -183,7 +183,7 @@ contentList: [ MultiInfoContentBlock { - blockHeaderAnimation: AnimationLoader.TRANSPORT_PIN + blockHeaderAnimation: AnimationLoader.Type.TRANSPORT_PIN //: LABEL ALL_PLATFORMS paragraphList: [ //: INFO ALL_PLATFORMS Answer to the question 'What is the Transport PIN?' @@ -196,7 +196,7 @@ //: INFO ALL_PLATFORMS Answer to the question 'Where do I find the Transport PIN?' paragraph 1/2 qsTr("The 5-digit Transport PIN is a %1one-time PIN%2, that you received per %1letter%2 when applying for the ID card.").arg("").arg(""), //: INFO ALL_PLATFORMS Answer to the question 'Where do I find the Transport PIN?' paragraph 2/2 - qsTr("When setting up your ID card, you will %1replace%2 this 5-digit %1Transport PIN with%2 a 6-digit, %1self chosen card PIN%2.").arg("").arg("")] + qsTr("When you set up your ID card, you will %1replace%2 this 5-digit %1Transport PIN with%2 a 6-digit, %1self chosen card PIN%2.").arg("").arg("")] } ] }, @@ -225,7 +225,7 @@ " • %1".arg(qsTr("You applied for PIN Reset Letter (letter containing a 6-digit PIN and corresponding Activation Code)."))] }, MultiInfoContentBlock { - blockHeaderAnimation: AnimationLoader.TRANSPORT_PIN + blockHeaderAnimation: AnimationLoader.Type.TRANSPORT_PIN paragraphList: [ //: INFO ALL_PLATFORMS Information that the Transport PIN is void once a 6-digit PIN was set. qsTr("Once you set a 6-digit PIN, the Transport PIN is no longer valid. You may then only use the 6-digit PIN to authenticate yourself.")] @@ -247,18 +247,18 @@ contentList: [ MultiInfoContentBlock { - blockHeaderAnimation: AnimationLoader.PUK + blockHeaderAnimation: AnimationLoader.Type.PUK //: LABEL ALL_PLATFORMS paragraphList: [ //: INFO ALL_PLATFORMS Answer to the question 'Where do I find the PUK?' - qsTr("The PUK is a %1 10-digit number%2 that you can find in the %1PIN letter%2 that was sent to you by mail after you %1applied for your ID card%2. You may find it to the %1right%2 of the 5-digit %1Transport PIN%2.").arg("").arg("")] + qsTr("The PUK is a%1 10-digit number%2 that you can find in the %1PIN letter%2 that was sent to you by mail after you %1applied for your ID card%2. You may find it to the %1right%2 of the 5-digit %1Transport PIN%2.").arg("").arg("")] }, MultiInfoContentBlock { //: LABEL ALL_PLATFORMS blockTitle: qsTr("Why is the PUK required?") paragraphList: [ //: INFO ALL_PLATFORMS Answer to the question 'Why is the PUK required?' - qsTr("The PUK is required if the %1card PIN has been entered incorrectly 3 times%2 in a row. As a result, the card PIN is blocked. By entering the PUK you will %1unblock the card PIN%2 and have %1 3 more attempts%2 to enter the correct PIN.").arg("").arg("")] + qsTr("The PUK is required if the %1card PIN has been entered incorrectly 3 times%2 in a row. As a result, the card PIN is blocked. By entering the PUK you will %1unblock the card PIN%2 and have%1 3 more attempts%2 to enter the correct PIN.").arg("").arg("")] }, MultiInfoContentBlock { //: LABEL ALL_PLATFORMS @@ -281,7 +281,7 @@ contentList: [ MultiInfoContentBlock { - blockHeaderAnimation: AnimationLoader.CAN + blockHeaderAnimation: AnimationLoader.Type.CAN paragraphList: [ //: INFO ALL_PLATFORMS Answer to the question 'When is the card access number (CAN) required?' qsTr("The card access number (CAN) is required when the %1card PIN has been entered incorrectly 2 times%2.").arg("").arg("")] @@ -313,7 +313,7 @@ contentList: [ MultiInfoContentBlock { - blockHeaderAnimation: AnimationLoader.CAN + blockHeaderAnimation: AnimationLoader.Type.CAN //: LABEL ALL_PLATFORMS blockTitle: qsTr("Why is the CAN required?") @@ -344,23 +344,23 @@ qsTr("Your ID card comes with a 5-digit %1Transport PIN%2 which you need to %1replace with%2 a 6-digit %1card PIN%2 that you choose yourself.").arg("").arg("")] }, MultiInfoContentBlock { - blockHeaderAnimation: AnimationLoader.TRANSPORT_PIN + blockHeaderAnimation: AnimationLoader.Type.TRANSPORT_PIN //: LABEL ALL_PLATFORMS blockTitle: qsTr("5-digit Transport PIN") paragraphList: [ //: INFO ALL_PLATFORMS Description text explaining the PINs 2/7 qsTr("The 5-digit Transport PIN is a %1one-time PIN%2 that was sent to you by %1mail%2 after you applied for your ID card.").arg("").arg(""), //: INFO ALL_PLATFORMS Description text explaining the PINs 3/7 - qsTr("When you set up the eID function, you will %1replace%2 this 5-digit %1Transport PIN%2 with a 6-digit %1card PIN that you choose yourself%2.").arg("").arg("")] + qsTr("When you set up your ID card, you will %1replace%2 this 5-digit %1Transport PIN with%2 a 6-digit, %1self chosen card PIN%2.").arg("").arg("")] }, MultiInfoContentBlock { - blockHeaderAnimation: AnimationLoader.PIN + blockHeaderAnimation: AnimationLoader.Type.PIN //: LABEL ALL_PLATFORMS blockTitle: qsTr("6-digit PIN") - paragraphList: ApplicationModel.isSmartSupported ? [ + paragraphList: ApplicationModel.smartSupported ? [ //: INFO ALL_PLATFORMS Description text explaining the PINs 4/7 - qsTr("The 6-digit card PIN is a %1number that you choose yourself%2 when you set up the eID function for the first time. It %1replaces%2 your %1 5-digit Transport PIN%2.").arg("").arg(""), + qsTr("The 6-digit card PIN is a %1number that you choose yourself%2 when you set up the eID function for the first time. It %1replaces%2 your%1 5-digit Transport PIN%2.").arg("").arg(""), //: INFO ALL_PLATFORMS Description text explaining the PINs 5/7 qsTr("The Smart-eID PIN also has six digits. You also choose that PIN yourself while setting up the Smart-eID for the first time."), //: INFO ALL_PLATFORMS Description text explaining the PINs 6/7 @@ -369,7 +369,7 @@ qsTr("You can change your card PIN and your Smart-eID PIN at any time in %1.").arg(Qt.application.name)] : [ //: INFO ALL_PLATFORMS Description text explaining the PINs 4/7 - qsTr("The 6-digit card PIN is a %1number that you choose yourself%2 when you set up the eID function for the first time. It %1replaces%2 your %1 5-digit Transport PIN%2.").arg("").arg(""), + qsTr("The 6-digit card PIN is a %1number that you choose yourself%2 when you set up the eID function for the first time. It %1replaces%2 your%1 5-digit Transport PIN%2.").arg("").arg(""), //: INFO ALL_PLATFORMS Description text explaining the PINs 6/7 qsTr("With this 6-digit PIN you prove online that the ID card belongs to you. %1No one can use the eID function without this PIN%2.").arg("").arg(""), //: INFO ALL_PLATFORMS Description text explaining the PINs (%1 is replaced with the application name) 7/7 @@ -408,7 +408,7 @@ contentList: [ MultiInfoContentBlock { - blockHeaderAnimation: AnimationLoader.PIN_UNKNOWN + blockHeaderAnimation: AnimationLoader.Type.PIN_UNKNOWN //: LABEL ALL_PLATFORMS blockTitle: qsTr("You do not know your PIN?") @@ -425,7 +425,7 @@ contentList: [ MultiInfoContentBlock { - blockAligment: Qt.AlignHCenter + blockAlignment: Qt.AlignHCenter paragraphList: [ //: LABEL DESKTOP qsTr("Set up the ID card in three steps")] @@ -459,7 +459,7 @@ contentList: [ MultiInfoContentBlock { - blockAligment: Qt.AlignHCenter + blockAlignment: Qt.AlignHCenter paragraphList: [ //: LABEL DESKTOP qsTr("Set up the ID card in two steps")] diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/MultiInfoView/internal/MultiInfoContentBlock.qml ausweisapp2-2.4.0/src/ui/qml/modules/MultiInfoView/internal/MultiInfoContentBlock.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/MultiInfoView/internal/MultiInfoContentBlock.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/MultiInfoView/internal/MultiInfoContentBlock.qml 2025-10-30 10:10:48.000000000 +0000 @@ -8,8 +8,8 @@ import Governikus.Animations QtObject { - property int blockAligment: Qt.AlignLeft - property int blockHeaderAnimation: AnimationLoader.NONE + property int blockAlignment: Qt.AlignLeft + property int blockHeaderAnimation: AnimationLoader.Type.NONE property url blockImage property string blockTitle property var paragraphList diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Navigation/+mobile/Navigation.qml ausweisapp2-2.4.0/src/ui/qml/modules/Navigation/+mobile/Navigation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Navigation/+mobile/Navigation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Navigation/+mobile/Navigation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -9,7 +9,7 @@ import Governikus.Style import Governikus.Type -RowLayout { +Item { id: root readonly property int activeModule: d.activeModule @@ -29,16 +29,22 @@ } enabled: !lockedAndHidden - height: lockedAndHidden ? 0 : (UiPluginModel.safeAreaMargins.bottom + implicitHeight) - visible: height > 0 + height: UiPluginModel.safeAreaMargins.bottom + navigationView.implicitHeight - Behavior on height { - id: heightAnimation + states: State { + when: d.lockedAndHidden - enabled: !ApplicationModel.isScreenReaderRunning + PropertyChanges { + root.height: UiPluginModel.safeAreaMargins.bottom + } + } + transitions: Transition { + enabled: !ApplicationModel.screenReaderRunning NumberAnimation { duration: Style.animation_duration + property: "height" + target: root } } @@ -52,16 +58,29 @@ Component.onCompleted: root.show(startupModule, initialLockedAndHidden) } - NavigationView { - id: navigationView - - Accessible.ignored: d.lockedAndHidden - Layout.alignment: Qt.AlignHCenter | Qt.AlignTop - activeModule: d.activeModule - - onShow: pModule => { - root.resetContentArea(); - root.show(pModule); + ColumnLayout { + anchors.left: parent.left + anchors.right: parent.right + + NavigationView { + id: navigationView + + Accessible.ignored: d.lockedAndHidden + Layout.alignment: Qt.AlignHCenter + activeModule: d.activeModule + visible: root.height > UiPluginModel.safeAreaMargins.bottom + + onShow: pModule => { + root.resetContentArea(); + root.show(pModule); + } } } + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + color: d.lockedAndHidden ? Style.color.background : Style.color.pane.background.basic + height: UiPluginModel.safeAreaMargins.bottom + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Navigation/+mobile/internal/NavigationItem.qml ausweisapp2-2.4.0/src/ui/qml/modules/Navigation/+mobile/internal/NavigationItem.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Navigation/+mobile/internal/NavigationItem.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Navigation/+mobile/internal/NavigationItem.qml 2025-10-30 10:10:48.000000000 +0000 @@ -2,13 +2,12 @@ * Copyright (c) 2015-2025 Governikus GmbH & Co. KG, Germany */ import QtQuick -import QtQuick.Controls import QtQuick.Layouts import Governikus.Global import Governikus.Style import Governikus.View -AbstractButton { +GAbstractButton { id: root property bool flowHorizontally: true @@ -56,7 +55,6 @@ Accessible.ignored: true Layout.alignment: root.flowHorizontally ? Qt.AlignLeft : Qt.AlignCenter Layout.preferredWidth: Math.min(Math.ceil(implicitWidth), root.contentItem.width) - activeFocusOnTab: false color: colors.textNormal elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/ChooseReaderType.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/ChooseReaderType.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/ChooseReaderType.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/ChooseReaderType.qml 2025-10-30 10:10:48.000000000 +0000 @@ -16,18 +16,15 @@ signal useUsbReader titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.leaveView() } - GText { - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter + Heading { //: LABEL DESKTOP text: qsTr("How would you like to read your ID card?") - textStyle: Style.text.headline } GSpacer { Layout.fillHeight: true diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingCheckIDView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingCheckIDView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingCheckIDView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingCheckIDView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + * Copyright (c) 2024-2025 Governikus GmbH & Co. KG, Germany */ pragma ComponentBehavior: Bound @@ -30,7 +30,7 @@ spacing: Style.dimens.pane_spacing titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.stopScanAndLeaveView() @@ -83,7 +83,7 @@ steps: 3 } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: { @@ -93,18 +93,45 @@ } onLeaveView: { - if (result !== CheckIDCardModel.Result.SUCCESS) { + switch (result) { + case CheckIDCardModel.Result.PIN_DEACTIVATED: + root.push(cardNotActivatedView); + break; + case CheckIDCardModel.Result.SUCCESS: + root.pop(); + root.continueOnboarding(); + break; + default: root.push(checkIDCardSuggestionView, { result: resultView.result }); - } else { - root.pop(); - root.continueOnboarding(); } } } } Component { + id: cardNotActivatedView + + CardNotActivatedView { + continueButtonVisible: true + title: root.title + + progress: ProgressTracker { + baseProgressTracker: root.progress + currentStep: 2 + steps: 3 + } + titleBarSettings: TitleBarSettings { + navigationAction: NavigationAction.Action.Back + startEnabled: false + + onNavigationActionClicked: root.pop() + } + + onContinueClicked: root.pinDeactivated() + } + } + Component { id: checkIDCardSuggestionView CheckIDCardSuggestionView { @@ -116,31 +143,28 @@ steps: 3 } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.pop() } onCheckSuccess: root.continueOnboarding() - onPinDeactivated: root.pinDeactivated() onRestartCheck: { root.pop(root); checkIDCardModel.startScan(root.pluginType); } } } - GText { - Layout.alignment: Text.AlignHCenter + Heading { //: INFO DESKTOP text: qsTr("Read ID card") - textStyle: Style.text.headline } AnimationLoader { Layout.alignment: Qt.AlignHCenter animated: false symbol: Symbol.Type.QUESTION - type: root.isPcsc ? AnimationLoader.WAIT_FOR_CARD_USB : AnimationLoader.WAIT_FOR_CARD_SAC + type: root.isPcsc ? AnimationLoader.Type.WAIT_FOR_CARD_USB : AnimationLoader.Type.WAIT_FOR_CARD_SAC } GText { id: subText diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingConfirmStageView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingConfirmStageView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingConfirmStageView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingConfirmStageView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -11,7 +11,7 @@ required property int stage titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.leaveView() diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingConnectUsbReaderView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingConnectUsbReaderView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingConnectUsbReaderView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingConnectUsbReaderView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -16,39 +16,27 @@ BaseOnboardingView { id: root - function continueWhenReaderFound() { - if (readerView.hasConnectedReader) - root.continueOnboarding(); - } - spacing: Style.dimens.pane_spacing titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.leaveView() } - Keys.onEnterPressed: root.continueWhenReaderFound() - Keys.onEscapePressed: root.leaveView() - Keys.onReturnPressed: root.continueWhenReaderFound() - ReaderScanEnabler { pluginType: ReaderManagerPluginType.PCSC } - GText { - Layout.alignment: Qt.AlignHCenter + Heading { Layout.bottomMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter + Layout.topMargin: -root.spacing //: LABEL DESKTOP text: qsTr("Set up card reader") - textStyle: Style.text.headline } - GText { + Subheading { //: LABEL DESKTOP text: qsTr("Connect an USB card reader") - textStyle: Style.text.subline } GText { text: readerView.hintTextBase diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingPairSacStartView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingPairSacStartView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingPairSacStartView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingPairSacStartView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,19 +14,16 @@ id: root titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.leaveView() } - GText { - Layout.alignment: Qt.AlignHCenter + Heading { Layout.bottomMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter //: LABEL DESKTOP text: qsTr("Set up smartphone as card reader") - textStyle: Style.text.headline } GridLayout { columnSpacing: Style.dimens.pane_spacing @@ -68,7 +65,7 @@ spacing: 0 GText { - font.weight: Font.Bold + font.weight: Style.font.bold text: "AusweisApp Bund" verticalAlignment: Text.AlignBottom } @@ -81,10 +78,9 @@ component PairSacPrerequisites: ColumnLayout { spacing: Style.dimens.groupbox_spacing - GText { + Subheading { //: LABEL DESKTOP %1 is replaced with the name AusweisApp text: "1. " + qsTr("Install %1 on your smartphone").arg(Qt.application.name) - textStyle: Style.text.subline } GText { @@ -102,10 +98,9 @@ component PairSacStart: ColumnLayout { spacing: Style.dimens.groupbox_spacing - GText { + Subheading { //: LABEL DESKTOP %1 is replaced with the name AusweisApp text: "2. " + qsTr("Open %1 on your smartphone").arg(Qt.application.name) - textStyle: Style.text.subline } GText { //: LABEL DESKTOP %1/%3 is replaced with bold highlighting, %2 with the name AusweisApp diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingPairSacView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingPairSacView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingPairSacView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingPairSacView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -20,7 +20,7 @@ spacing: Style.dimens.pane_spacing titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.leaveView() @@ -29,13 +29,11 @@ ReaderScanEnabler { pluginType: ReaderManagerPluginType.REMOTE_IFD } - GText { - Layout.alignment: Qt.AlignHCenter + Heading { Layout.bottomMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter + Layout.topMargin: -root.spacing //: LABEL DESKTOP text: qsTr("Set up smartphone as card reader") - textStyle: Style.text.headline } GPane { Layout.fillWidth: true @@ -46,9 +44,9 @@ backgroundColor: Style.color.transparent drawBottomCorners: true drawTopCorners: true - expanded: true //: LABEL DESKTOP selectionTitle: expanded ? "" : qsTr("Click the arrow to show.") + startExpanded: true //: LABEL DESKTOP title: qsTr("Pairing instructions") @@ -56,10 +54,11 @@ } } } - GText { + Subheading { + id: deviceListHeading + //: LABEL DESKTOP text: qsTr("Available devices") - textStyle: Style.text.subline } AvailableDevices { } @@ -72,7 +71,7 @@ title: root.title titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } @@ -87,7 +86,6 @@ component AvailableDevices: GPane { Layout.fillWidth: true - spacing: Style.dimens.pane_spacing Repeater { id: devicesInPairingMode @@ -112,12 +110,9 @@ model: RemoteServiceModel.availablePairedDevices delegate: RemoteReaderDelegate { - //: LABEL DESKTOP - Accessible.name: qsTr("Press space to continue onboarding using the smartphone \"%1\"").arg(remoteDeviceName) Layout.fillWidth: true customContent: GButton { - activeFocusOnTab: !ApplicationModel.isScreenReaderRunning //: LABEL DESKTOP text: qsTr("Use device") @@ -126,11 +121,6 @@ root.positionViewAtItem(this) } - Keys.onSpacePressed: { - if (ApplicationModel.isScreenReaderRunning) { - root.continueOnboarding(); - } - } onFocusChanged: if (focus) Utils.positionViewAtItem(this) } @@ -165,10 +155,10 @@ progress: root.progress infoContent: MultiInfoData { - contentType: MultiInfoData.NO_SAC_FOUND + contentType: MultiInfoData.Type.NO_SAC_FOUND } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.pop() @@ -178,7 +168,7 @@ } component FramedImage: Image { Layout.alignment: Qt.AlignHCenter - sourceSize.width: UiPluginModel.scaleFactor * 380 + sourceSize.width: UiPluginModel.scaleFactor * 228 Rectangle { anchors.fill: parent @@ -218,7 +208,6 @@ } GText { Accessible.ignored: true - activeFocusOnTab: false //: LABEL DESKTOP text: qsTr("or") } @@ -227,8 +216,8 @@ } } GText { - //: LABEL DESKTOP - text: "5. " + qsTr("Afterwards, a pairing code is displayed on your smartphone. You may then select the device in the list below to enter the pairing code.") + //: LABEL DESKTOP %1 will be replaced with the Available Devices list name + text: "5. " + qsTr("Afterwards, a pairing code is displayed on your smartphone. You may then select the device in the %1 list to enter the pairing code.").arg("" + deviceListHeading.text + "") } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/OnboardingView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/OnboardingView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -88,7 +88,7 @@ steps: 6 } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.pop() @@ -294,12 +294,31 @@ ChangePinView { activateUI: false - navigationActionType: NavigationAction.Back + navigationActionType: NavigationAction.Action.Back onlyCheckPin: true skipInfoView: false title: root.title usedInOnboarding: true + cardNotActivatedDelegate: CardNotActivatedView { + continueButtonVisible: true + title: root.title + + progress: ProgressTracker { + baseProgressTracker: progressTracker + currentStage: 3 + relativeProgress: 1 + } + titleBarSettings: TitleBarSettings { + navigationAction: NavigationAction.Action.Back + navigationEnabled: false + } + + onContinueClicked: { + root.onboardingAborted = true; + ChangePinModel.continueWorkflow(); + } + } errorViewDelegate: OnboardingPinErrorView { title: root.title @@ -309,7 +328,7 @@ relativeProgress: 1 } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back navigationEnabled: false } @@ -338,7 +357,7 @@ relativeProgress: 1 } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back navigationEnabled: false } @@ -346,7 +365,7 @@ onLeaveView: ChangePinModel.continueWorkflow() } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.pop() @@ -372,7 +391,7 @@ title: root.title titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Cancel + navigationAction: NavigationAction.Action.Cancel startEnabled: false onNavigationActionClicked: root.continueToAutostartView() @@ -408,7 +427,7 @@ title: root.title titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back navigationEnabled: false startEnabled: false } @@ -442,7 +461,7 @@ title: root.title titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.pop() diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/PreparationInfoView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/PreparationInfoView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/PreparationInfoView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/PreparationInfoView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -18,52 +18,42 @@ signal abortWithUnknownPin titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.leaveView() } - Keys.onEnterPressed: root.continueOnboarding() - Keys.onEscapePressed: root.leaveView() - Keys.onReturnPressed: root.continueOnboarding() - - GText { - Layout.alignment: Qt.AlignHCenter + Heading { Layout.bottomMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter //: LABEL DESKTOP text: qsTr("What is required to use %1?").arg(Qt.application.name) - textStyle: Style.text.headline } - GText { + Subheading { //: LABEL DESKTOP text: "1. " + qsTr("Your ID card") - textStyle: Style.text.subline } GText { Layout.bottomMargin: Style.dimens.pane_spacing //: LABEL DESKTOP %1 + %2 = Bold Tags text: qsTr("You could either use the %1German ID card%2, an %1electronic residence permit%2 or the %1eID card for EU citizens%2.").arg("").arg("") } - GText { + Subheading { //: LABEL DESKTOP text: "2. " + qsTr("A smartphone or a card reader") - textStyle: Style.text.subline } GText { Layout.bottomMargin: Style.dimens.pane_spacing //: LABEL DESKTOP %1 = Application Name, %2 + %3 = Bold Tags text: qsTr("You may use your %2NFC-enabled smartphone%3. To do this you will have to install %1 on this smartphone. Alternatively you may use an %2USB card reader with your PC%3.").arg(Qt.application.name).arg("").arg("") } - GText { + Subheading { //: LABEL DESKTOP text: "3. " + qsTr("Your PIN") - textStyle: Style.text.subline } GText { //: LABEL DESKTOP %1 + %2 = Bold Tags, %3 = AusweisApp - text: qsTr("You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a %1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, click here:").arg("").arg("").arg(Qt.application.name) + text: qsTr("You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a%1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, click here:").arg("").arg("").arg(Qt.application.name) } MoreInformationLink { Layout.bottomMargin: Style.dimens.pane_spacing * 2 @@ -86,10 +76,10 @@ progress: root.progress infoContent: MultiInfoData { - contentType: MultiInfoData.NO_PIN + contentType: MultiInfoData.Type.NO_PIN } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.pop() diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/SkipOnboardingConfirmation.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/SkipOnboardingConfirmation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+desktop/SkipOnboardingConfirmation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+desktop/SkipOnboardingConfirmation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -12,7 +12,7 @@ qsTr("Only skip this step, if you already set up a 6-digit ID card PIN and a card reader."), root.commonDescriptionText] titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back startEnabled: false onNavigationActionClicked: root.leaveView() diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+mobile/OnboardingCheckIDView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+mobile/OnboardingCheckIDView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+mobile/OnboardingCheckIDView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+mobile/OnboardingCheckIDView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,5 +1,5 @@ /** - * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + * Copyright (c) 2024-2025 Governikus GmbH & Co. KG, Germany */ pragma ComponentBehavior: Bound @@ -76,7 +76,7 @@ usedInOnboarding: true navigationAction: NavigationAction { - action: NavigationAction.Back + action: NavigationAction.Action.Back onClicked: root.leaveView() } @@ -88,18 +88,45 @@ } onContinueClicked: { - if (result !== CheckIDCardModel.Result.SUCCESS) { + switch (result) { + case CheckIDCardModel.Result.PIN_DEACTIVATED: + root.push(cardNotActivatedView); + break; + case CheckIDCardModel.Result.SUCCESS: + root.pop(); + root.continueOnboarding(); + break; + default: root.push(checkIDCardSuggestionView, { result: resultView.result }); - } else { - root.pop(); - root.continueOnboarding(); } } } } Component { + id: cardNotActivatedView + + CardNotActivatedView { + continueButtonVisible: true + title: root.title + + navigationAction: NavigationAction { + action: NavigationAction.Action.Back + + onClicked: root.pop() + } + progress: ProgressTracker { + baseProgressTracker: root.progress + currentStage: 1 + currentStep: 5 + steps: 6 + } + + onContinueClicked: root.pinDeactivated() + } + } + Component { id: checkIDCardSuggestionView CheckIDCardSuggestionView { @@ -118,7 +145,6 @@ } onCheckSuccess: root.continueOnboarding() - onPinDeactivated: root.pinDeactivated() onRestartCheck: { root.pop(root); checkConnection.enabled = true; @@ -126,17 +152,15 @@ } } } - GText { - Layout.alignment: Text.AlignHCenter + Heading { //: LABEL IOS ANDROID text: qsTr("Read ID card") - textStyle: Style.text.headline } AnimationLoader { Layout.alignment: Qt.AlignHCenter animated: false symbol: Symbol.Type.QUESTION - type: AnimationLoader.WAIT_FOR_CARD_SAC + type: AnimationLoader.Type.WAIT_FOR_CARD_SAC } GText { id: subText diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+mobile/OnboardingView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+mobile/OnboardingView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+mobile/OnboardingView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+mobile/OnboardingView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -73,7 +73,7 @@ id: setupInfoView MultiInfoView { - //: LABEL DESKTOP + //: LABEL ANDROID IOS continueButtonText: qsTr("Continue") title: root.title @@ -252,6 +252,25 @@ title: root.title usedInOnboarding: true + cardNotActivatedDelegate: CardNotActivatedView { + continueButtonVisible: true + title: root.title + + navigationAction: NavigationAction { + action: NavigationAction.Action.Back + enabled: false + } + progress: ProgressTracker { + baseProgressTracker: progressTracker + currentStage: 2 + relativeProgress: 1 + } + + onContinueClicked: { + ChangePinModel.continueWorkflow(); + changePinViewInstance.abortOnboarding = true; + } + } errorViewDelegate: OnboardingPinErrorView { title: root.title diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+mobile/PreparationInfoView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+mobile/PreparationInfoView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+mobile/PreparationInfoView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+mobile/PreparationInfoView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -23,18 +23,14 @@ onClicked: root.leaveView() } - GText { - Layout.alignment: Qt.AlignHCenter + Heading { Layout.bottomMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter //: LABEL ANDROID IOS text: qsTr("What is required to use %1?").arg(Qt.application.name) - textStyle: Style.text.headline } - GText { + Subheading { //: LABEL ANDROID IOS text: "1. " + qsTr("Your ID card") - textStyle: Style.text.subline } GText { //: LABEL ANDROID IOS @@ -51,10 +47,9 @@ //: LABEL ANDROID IOS listelement.arg(qsTr("eID card for EU citizens")) + "" } - GText { + Subheading { //: LABEL ANDROID IOS text: "2. " + qsTr("A NFC-enabled smartphone") - textStyle: Style.text.subline } GText { Layout.bottomMargin: Style.dimens.pane_spacing @@ -62,14 +57,13 @@ //: LABEL ANDROID IOS %1 + %2 = Bold Tags text: qsTr("The chip in your ID card is read using %1NFC%2. To do this, simply place the ID card on the %1back of the smartphone%2.").arg("").arg("") } - GText { + Subheading { //: LABEL ANDROID IOS text: "3. " + qsTr("Your PIN") - textStyle: Style.text.subline } GText { //: LABEL ANDROID IOS %1 + %2 = Bold Tags, %3 = AusweisApp - text: qsTr("You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a %1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, click here:").arg("").arg("").arg(Qt.application.name) + text: qsTr("You have received a one-time PIN, the %1Transport PIN%2, as a letter from your competent authority. You can replace this with a%1 6-digit card PIN%2 in the %3 or at the Citizens' Registration Office. If you do not have a PIN or do not remember your card PIN, tap here:").arg("").arg("").arg(Qt.application.name) } MoreInformationLink { Layout.bottomMargin: Style.dimens.pane_spacing * 2 @@ -92,7 +86,7 @@ progress: root.progress infoContent: MultiInfoData { - contentType: MultiInfoData.NO_PIN + contentType: MultiInfoData.Type.NO_PIN } navigationAction: NavigationAction { action: NavigationAction.Action.Back diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+mobile/PrimaryDeviceDecisionView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+mobile/PrimaryDeviceDecisionView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+mobile/PrimaryDeviceDecisionView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+mobile/PrimaryDeviceDecisionView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -20,13 +20,9 @@ onClicked: root.leaveView() } - GText { - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - + Heading { //: LABEL ANDROID IOS text: qsTr("Do you want to also use the %1 on a PC/Mac?").arg(Qt.application.name) - textStyle: Style.text.headline wrapMode: Text.WordWrap } GText { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+mobile/SetupDesktopUsage.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+mobile/SetupDesktopUsage.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/+mobile/SetupDesktopUsage.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/+mobile/SetupDesktopUsage.qml 2025-10-30 10:10:48.000000000 +0000 @@ -23,13 +23,10 @@ onClicked: root.leaveView() } - GText { - Layout.alignment: Qt.AlignHCenter + Heading { Layout.bottomMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter //: LABEL ANDROID IOS text: qsTr("Set up %1 on the PC/Mac").arg(Qt.application.name) - textStyle: Style.text.headline } TintableIcon { Layout.alignment: Qt.AlignHCenter @@ -38,10 +35,9 @@ sourceSize.height: Style.dimens.medium_icon_size tintColor: Style.color.image } - GText { + Subheading { //: LABEL ANDROID IOS text: "1. " + qsTr("Install %1").arg(Qt.application.name) - textStyle: Style.text.subline } GText { //: LABEL ANDROID IOS %1 is replaced with the name "AusweisApp" @@ -53,10 +49,9 @@ Layout.bottomMargin: Style.dimens.pane_spacing text: "%1".arg(link) } - GText { + Subheading { //: LABEL ANDROID IOS %1 is replaced with the name "AusweisApp" text: "2. " + qsTr("Open %1").arg(Qt.application.name) - textStyle: Style.text.subline } GText { Layout.bottomMargin: Style.dimens.pane_spacing @@ -64,16 +59,15 @@ //: LABEL ANDROID IOS %1 is replaced with the name "AusweisApp" text: qsTr("Open the %1 on your PC/Mac.").arg(Qt.application.name) } - GText { + Subheading { //: LABEL ANDROID IOS text: "3. " + qsTr("Follow the instruction on your second device") - textStyle: Style.text.subline } GText { Layout.bottomMargin: Style.dimens.pane_spacing //: LABEL ANDROID IOS - text: qsTr("Now follow the instruction for the setup on your PC/Mac. If the onboarding does not start automatically, you may find it under Help > Onboarding.") + text: qsTr("Now follow the instruction for the setup on your PC/Mac. If the setup does not start automatically, you may find it under Help > Setup.") } GSpacer { Layout.fillHeight: true diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/OnboardingConfirmStageBaseView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/OnboardingConfirmStageBaseView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/OnboardingConfirmStageBaseView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/OnboardingConfirmStageBaseView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -16,17 +16,10 @@ signal continueOnboarding - Keys.onEnterPressed: root.continueOnboarding() - Keys.onEscapePressed: root.continueOnboarding() - Keys.onReturnPressed: root.continueOnboarding() - - GText { + Heading { id: titleText - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter text: confirmationData.headerText - textStyle: Style.text.headline wrapMode: Text.WordWrap } AnimationLoader { @@ -36,12 +29,11 @@ symbol: confirmationData.animationSymbol type: confirmationData.animationType } - GText { + Subheading { Layout.topMargin: Style.dimens.pane_spacing text: confirmationData.sublineText !== "" ? confirmationData.sublineText : //: LABEL ALL_PLATFORMS qsTr("Step %1 of %2 was successful").arg(root.progress.currentStage).arg(root.progress.stages) - textStyle: Style.text.subline } GText { id: descriptionText diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/OnboardingConfirmationViewData.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/OnboardingConfirmationViewData.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/OnboardingConfirmationViewData.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/OnboardingConfirmationViewData.qml 2025-10-30 10:10:48.000000000 +0000 @@ -23,17 +23,17 @@ switch (stage) { case OnboardingConfirmationViewData.Stage.CHANGE_PIN: case OnboardingConfirmationViewData.Stage.PIN_CORRECT: - return AnimationLoader.CHANGEPIN_SUCCESS; + return AnimationLoader.Type.CHANGEPIN_SUCCESS; case OnboardingConfirmationViewData.Stage.CHECK_ID_CARD_CARD_READER: - return AnimationLoader.WAIT_FOR_CARD_USB; + return AnimationLoader.Type.WAIT_FOR_CARD_USB; case OnboardingConfirmationViewData.Stage.CHECK_ID_CARD_SAC: - return AnimationLoader.CARD_RESULT; + return AnimationLoader.Type.CARD_RESULT; case OnboardingConfirmationViewData.Stage.PAIR_DEVICE: - return AnimationLoader.SAC_CONNECTION; + return AnimationLoader.Type.SAC_CONNECTION; case OnboardingConfirmationViewData.Stage.READER_DETECTED: - return AnimationLoader.WAIT_FOR_READER; + return AnimationLoader.Type.WAIT_FOR_READER; default: - return AnimationLoader.NONE; + return AnimationLoader.Type.NONE; } } //: LABEL ALL_PLATFORMS Common part of the description of the confirmation view after the set up AusweisApp (with SaC) stage. diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/OnboardingFailedView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/OnboardingFailedView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/OnboardingFailedView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/OnboardingFailedView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -18,12 +18,9 @@ readonly property bool continuesToAutoStart: numberOfPrompts > 0 readonly property int numberOfPrompts: Style.is_layout_desktop ? (SettingsModel.autoStartAvailable && !SettingsModel.autoStartSetByAdmin ? 2 : 1) : 0 } - GText { - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter + Heading { //: LABEL ALL_PLATFORMS text: qsTr("Requirements not met") - textStyle: Style.text.headline } TintableIcon { Layout.alignment: Qt.AlignHCenter @@ -32,11 +29,10 @@ sourceSize.height: Style.dimens.header_icon_size tintColor: Style.color.image } - GText { + Subheading { Layout.topMargin: Style.dimens.pane_spacing //: LABEL ALL_PLATFORMS %1 is replaced with the name "AusweisApp". text: qsTr("You cannot use the %1").arg(Qt.application.name) - textStyle: Style.text.subline } GText { Layout.topMargin: Style.dimens.text_spacing @@ -57,7 +53,7 @@ } //: LABEL ALL_PLATFORMS %1 and %2 are replaced with bold emphasis. - readonly property string restartOnboardingText: qsTr("You may restart the setup anytime under %1Help > Onboarding%2.").arg("").arg("") + readonly property string restartOnboardingText: qsTr("You may restart the setup anytime under %1Help > Setup%2.").arg("").arg("") Layout.topMargin: Style.dimens.text_spacing text: abortText + " " + restartOnboardingText diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/OnboardingStartView.qml ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/OnboardingStartView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/OnboardingView/OnboardingStartView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/OnboardingView/OnboardingStartView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -18,13 +18,9 @@ //: LABEL ALL_PLATFORMS Headline title: qsTr("Setup") - GText { - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - + Heading { //: LABEL ALL_PLATFORMS text: qsTr("Set up the eID function") - textStyle: Style.text.headline wrapMode: Text.WordWrap } SiteWithLogoAnimation { @@ -32,11 +28,10 @@ Layout.topMargin: Style.dimens.pane_spacing sourceSize.height: Style.dimens.header_icon_size } - GText { + Subheading { Layout.topMargin: Style.dimens.pane_spacing //: LABEL ALL_PLATFORMS text: qsTr("What can I do with it?") - textStyle: Style.text.subline } GText { readonly property string technologyString: Style.is_layout_desktop ? diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ProgressView/+desktop/ProgressView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ProgressView/+desktop/ProgressView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ProgressView/+desktop/ProgressView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ProgressView/+desktop/ProgressView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -13,6 +13,7 @@ property alias headline: headline.text property alias icon: statusIcon.source + property string progressBarA11yText: "" property alias progressBarVisible: progressBar.visible property alias progressText: progressText.text property alias progressValue: progressBar.value @@ -40,11 +41,9 @@ topMargin: Style.dimens.pane_spacing } } - GText { + Heading { id: headline - horizontalAlignment: Text.AlignHCenter - textStyle: Style.text.headline visible: text !== "" width: Math.min(parent.width - (2 * Style.dimens.pane_padding), Style.dimens.max_text_width) @@ -83,13 +82,13 @@ GProgressBar { id: progressBar - activeFocusOnTab: true + Accessible.name: root.progressBarA11yText visible: false anchors { bottom: parent.bottom left: parent.left - margins: UiPluginModel.scaleFactor * 80 + margins: UiPluginModel.scaleFactor * 48 right: parent.right } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ProgressView/+mobile/ProgressView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ProgressView/+mobile/ProgressView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ProgressView/+mobile/ProgressView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ProgressView/+mobile/ProgressView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -13,6 +13,7 @@ property alias headline: headline.text property alias icon: statusIcon.source + property string progressBarA11yText: "" property alias progressBarVisible: progressBar.visible property alias progressText: progressText.text property alias progressValue: progressBar.value @@ -30,13 +31,10 @@ tintColor: Style.color.image visible: source.toString() !== "" } - GText { + Heading { id: headline - Layout.alignment: Qt.AlignHCenter Layout.topMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter - textStyle: Style.text.headline visible: text !== "" } GText { @@ -53,6 +51,7 @@ GProgressBar { id: progressBar + Accessible.name: root.progressBarA11yText Layout.alignment: Qt.AlignHCenter Layout.fillWidth: true Layout.topMargin: 2 * Style.dimens.pane_spacing diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/+mobile/RemoteServiceSettings.qml ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/+mobile/RemoteServiceSettings.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/+mobile/RemoteServiceSettings.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/+mobile/RemoteServiceSettings.qml 2025-10-30 10:10:48.000000000 +0000 @@ -5,6 +5,7 @@ pragma ComponentBehavior: Bound import QtQuick +import QtQuick.Layouts import Governikus.EnterPasswordView import Governikus.Global @@ -45,23 +46,21 @@ } GOptionsContainer { containerPadding: Style.dimens.pane_padding + containerSpacing: Style.dimens.text_spacing //: LABEL ANDROID IOS title: qsTr("Paired devices") visible: availablePairedDeviceList.count > 0 - ListView { + Repeater { id: availablePairedDeviceList - height: childrenRect.height - interactive: false model: RemoteServiceModel.availablePairedDevices - spacing: Style.dimens.pane_spacing - width: parent.width delegate: DevicesListDelegate { + Layout.fillWidth: true description: root.allowUsage ? //: LABEL ANDROID IOS - qsTr("Click to use device") : + qsTr("Tap to use device") : //: LABEL ANDROID IOS qsTr("Available") width: availablePairedDeviceList.width @@ -73,22 +72,20 @@ } GOptionsContainer { containerPadding: Style.dimens.pane_padding + containerSpacing: Style.dimens.text_spacing //: LABEL ANDROID IOS title: qsTr("Last connected") visible: unavailablePairedDeviceList.count > 0 - ListView { + Repeater { id: unavailablePairedDeviceList - height: childrenRect.height - interactive: false model: RemoteServiceModel.unavailablePairedDevices - spacing: Style.dimens.pane_spacing - width: parent.width delegate: DevicesListDelegate { + Layout.fillWidth: true //: LABEL ANDROID IOS - description: qsTr("Click to remove device") + description: qsTr("Tap to remove device") width: unavailablePairedDeviceList.width onActivate: (pIsSupported, pDeviceId) => { @@ -117,22 +114,20 @@ } GOptionsContainer { containerPadding: Style.dimens.pane_padding - containerSpacing: Style.dimens.pane_spacing + containerSpacing: Style.dimens.text_spacing //: LABEL ANDROID IOS title: qsTr("Add pairing") - GListView { + Repeater { id: searchDeviceList - height: contentHeight model: RemoteServiceModel.availableDevicesInPairingMode - spacing: Style.dimens.pane_spacing visible: ApplicationModel.wifiEnabled && count > 0 - width: parent.width delegate: DevicesListDelegate { + Layout.fillWidth: true //: LABEL ANDROID IOS - description: qsTr("Click to pair") + description: qsTr("Tap to pair") width: searchDeviceList.width onActivate: (pIsSupported, pDeviceId) => { @@ -148,10 +143,9 @@ //: INFO ANDROID IOS Wifi is not enabled and no new devices can be paired. text: qsTr("Please connect your WiFi to use another smartphone as card reader (SaC).") visible: !ApplicationModel.wifiEnabled - width: parent.width } GButton { - anchors.horizontalCenter: parent.horizontalCenter + Layout.alignment: Qt.AlignHCenter //: LABEL ANDROID IOS text: qsTr("Enable WiFi") @@ -161,11 +155,9 @@ } LocalNetworkInfo { visible: RemoteServiceModel.requiresLocalNetworkPermission && !RemoteServiceModel.remoteReaderVisible - width: parent.width } PairingProcessInfo { visible: !searchDeviceList.visible && ApplicationModel.wifiEnabled - width: parent.width onInfoLinkClicked: root.push(noSacFoundInfo) } @@ -176,7 +168,7 @@ progress: root.progress infoContent: MultiInfoData { - contentType: MultiInfoData.NO_SAC_FOUND + contentType: MultiInfoData.Type.NO_SAC_FOUND } navigationAction: NavigationAction { action: NavigationAction.Action.Back diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/+mobile/RemoteServiceView.qml ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/+mobile/RemoteServiceView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/+mobile/RemoteServiceView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/+mobile/RemoteServiceView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -7,6 +7,7 @@ import QtQuick import QtQuick.Layouts +import Governikus.Animations import Governikus.Global import Governikus.Style import Governikus.TitleBar @@ -18,8 +19,12 @@ id: root readonly property int nfcState: ApplicationModel.nfcState + readonly property bool notRunningNoPairingMode: !RemoteServiceModel.running && !RemoteServiceModel.isPairing + readonly property bool runnableNoPairedDevice: RemoteServiceModel.runnable && knownDeviceList.count === 0 && !RemoteServiceModel.isPairing + readonly property bool runningInPairingMode: RemoteServiceModel.running && RemoteServiceModel.isPairing enableTileStyle: false + spacing: Style.dimens.pane_spacing //: LABEL ANDROID IOS title: qsTr("Card reader") @@ -32,7 +37,7 @@ states: [ State { name: "PAIRING" - when: RemoteServiceModel.running && RemoteServiceModel.isPairing + when: root.runningInPairingMode PropertyChanges { knownDevicesContainer.visible: false @@ -46,7 +51,6 @@ PropertyChanges { networkPermissionText.visible: false - wifiInfo.visible: false } } ] @@ -63,229 +67,167 @@ stackView: root.stackView } - GOptionsContainer { - containerPadding: Style.dimens.pane_padding - //: LABEL ANDROID IOS - title: !ApplicationModel.wifiEnabled ? qsTr("WiFi not active") : - //: LABEL ANDROID IOS - RemoteServiceModel.canEnableNfc ? qsTr("NFC not active") : - //: LABEL ANDROID IOS - !RemoteServiceModel.runnable ? qsTr("NFC is not available") : - //: LABEL ANDROID IOS - RemoteServiceModel.connectedToPairedDevice ? qsTr("Card access in progress") : - //: LABEL ANDROID IOS - RemoteServiceModel.isPairing ? qsTr("Waiting for pairing") : - //: LABEL ANDROID IOS - RemoteServiceModel.running ? qsTr("Card reader ready") : "" + ColumnLayout { + Layout.topMargin: -root.spacing + spacing: root.spacing visible: root.nfcState !== ApplicationModel.NfcState.UNAVAILABLE - ColumnLayout { - spacing: Style.dimens.pane_spacing - width: parent.width - - TintableIcon { - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: Style.dimens.text_spacing - source: "qrc:///images/phone_to_pc.svg" - sourceSize.height: Style.dimens.medium_icon_size - tintColor: Style.color.image - } - GText { - id: infoText - - readonly property string currentPin: RemoteServiceModel.psk - //: INFO ANDROID IOS - readonly property string enterCodeString: qsTr("Enter the pairing code \"%1\" in the %2 on your other device.") - - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: Style.dimens.text_spacing - horizontalAlignment: Text.AlignHCenter - //: INFO ANDROID IOS - text: qsTr("You can use this Smartphone as a card reader for the %1 on other devices e.g. a laptop.\n\nTo do this you first have to pair that device with this smartphone.").arg(Qt.application.name) - - states: [ - State { - when: !RemoteServiceModel.runnable && RemoteServiceModel.errorMessage !== "" - - PropertyChanges { - infoText.text: RemoteServiceModel.errorMessage - } - }, - State { - when: RemoteServiceModel.running && RemoteServiceModel.connectedToPairedDevice - - PropertyChanges { - infoText.text: RemoteServiceModel.connectionInfo - } - }, - State { - when: RemoteServiceModel.isPairing - - PropertyChanges { - infoText.Accessible.name: infoText.enterCodeString.arg(infoText.currentPin.split("").join(" ")).arg(Qt.application.name) - infoText.text: infoText.enterCodeString.arg(infoText.currentPin).arg(Qt.application.name) - } - }, - State { - when: !RemoteServiceModel.running && knownDeviceList.count > 0 - - PropertyChanges { - //: INFO ANDROID IOS - infoText.text: qsTr("Activate the card reader, this allows the paired devices to use this smartphone as a card reader.") - } - }, - State { - when: RemoteServiceModel.running && knownDeviceList.count > 0 - - PropertyChanges { - infoText.text: "%1\n\n%2" - //: INFO ANDROID IOS - .arg(qsTr("Paired devices may use this Smartphone as a card reader now.")) - //: INFO ANDROID IOS - .arg(qsTr("To do this, start a process on a paired device.")) - } - } - ] - } - GText { - id: pairingCode - - readonly property string currentPin: RemoteServiceModel.psk - - Accessible.ignored: true - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter - - //: LABEL ANDROID IOS - text: qsTr("Pairing code: %1").arg(RemoteServiceModel.isPairing ? currentPin : "0000") - textStyle: Style.text.headline - visible: false - } - MoreInformationLink { - id: paringCodeLink - - Layout.alignment: Qt.AlignCenter - Layout.topMargin: Style.dimens.pane_spacing - //: LABEL ANDROID IOS - text: qsTr("Where do I enter the pairing code?") - visible: false - - onClicked: root.push(pairingCodeInfoView) - - Component { - id: pairingCodeInfoView + Heading { + //: LABEL ANDROID IOS + text: !ApplicationModel.wifiEnabled ? qsTr("WiFi not active") : + //: LABEL ANDROID IOS + RemoteServiceModel.canEnableNfc ? qsTr("NFC not active") : + //: LABEL ANDROID IOS + !RemoteServiceModel.runnable ? qsTr("NFC is not available") : + //: LABEL ANDROID IOS + RemoteServiceModel.connectedToPairedDevice ? qsTr("Card access in progress") : + //: LABEL ANDROID IOS + RemoteServiceModel.isPairing ? qsTr("Waiting for pairing") : + //: LABEL ANDROID IOS + RemoteServiceModel.running ? qsTr("Card reader ready") : + //: LABEL ANDROID IOS + root.runnableNoPairedDevice ? qsTr("No device paired") : + //: LABEL ANDROID IOS + RemoteServiceModel.runnable && knownDeviceList.count > 0 && root.notRunningNoPairingMode ? qsTr("Card reader not active") : "" + visible: text !== "" + } + TintableIcon { + id: pairingIcon - PairingCodeInfoView { - text: paringCodeLink.text + Layout.alignment: Qt.AlignHCenter + source: "qrc:///images/phone_to_pc.svg" + sourceSize.height: Style.dimens.header_icon_size + tintColor: Style.color.image + visible: knownDeviceList.count === 0 || root.runningInPairingMode + } + AnimationLoader { + Layout.alignment: Qt.AlignHCenter + animated: false + symbol: RemoteServiceModel.running ? Symbol.Type.CHECK : Symbol.Type.ERROR + type: AnimationLoader.Type.WAIT_FOR_SAC + visible: !pairingIcon.visible + } + Subheading { + id: subline - onNavActionClicked: root.pop() + states: [ + State { + when: root.runnableNoPairedDevice + + PropertyChanges { + //: LABEL ANDROID IOS + subline.text: qsTr("Use this smartphone as a card reader for a paired device") + } + }, + State { + when: RemoteServiceModel.runnable && knownDeviceList.count > 0 && root.notRunningNoPairingMode + + PropertyChanges { + //: LABEL ANDROID IOS + subline.text: qsTr("Activate the card reader") + } + }, + State { + when: RemoteServiceModel.running && !RemoteServiceModel.isPairing + + PropertyChanges { + //: LABEL ANDROID IOS + subline.text: qsTr("Paired devices may use this Smartphone as a card reader now.") + } + }, + State { + when: root.runningInPairingMode + + PropertyChanges { + //: LABEL ANDROID IOS + subline.text: qsTr("Enter the pairing code") } } - } - } - } - NfcWorkflow { - id: nfcWorkflow - - Layout.fillHeight: true - Layout.fillWidth: true - bottomMargin: 0 - isRemoteWorkflow: true - leftMargin: 0 - rightMargin: 0 - topMargin: 0 - visible: root.nfcState === ApplicationModel.NfcState.UNAVAILABLE - - onShowRemoteServiceSettings: root.push(remoteServiceSettings) - - Component { - id: remoteServiceSettings - - RemoteServiceSettings { - Component.onCompleted: RemoteServiceModel.startDetection() - Component.onDestruction: RemoteServiceModel.stopDetection(true) - } + ] } - } - GOptionsContainer { - id: knownDevicesContainer - - Layout.topMargin: Style.dimens.pane_spacing - containerPadding: Style.dimens.pane_padding - //: LABEL ANDROID IOS - title: qsTr("Paired Devices") - visible: RemoteServiceModel.runnable && knownDeviceList.count > 0 - - ColumnLayout { - id: knownDevices - - spacing: Style.dimens.text_spacing - width: parent.width + GText { + id: infoText - Repeater { - id: knownDeviceList + readonly property string currentPin: RemoteServiceModel.psk + //: INFO ANDROID IOS %1 is replaced with the pairing code, %2 with the name "AusweisApp" + readonly property string enterCodeString: qsTr("Enter the pairing code \"%1\" in the %2 on your other device. Both devices have to be on the same network (e.g. WiFi).") + + //: INFO ANDROID IOS + text: qsTr("You can use this Smartphone as a card reader for the %1 on other devices e.g. a laptop.\n\nTo do this you first have to pair that device with this smartphone.").arg(Qt.application.name) + + states: [ + State { + when: !RemoteServiceModel.runnable && RemoteServiceModel.errorMessage !== "" - model: RemoteServiceModel.allDevices + PropertyChanges { + infoText.text: RemoteServiceModel.errorMessage + } + }, + State { + when: RemoteServiceModel.running && RemoteServiceModel.connectedToPairedDevice - delegate: DevicesListDelegate { - Layout.fillWidth: true - linkQualityVisible: false + PropertyChanges { + infoText.text: RemoteServiceModel.connectionInfo + } + }, + State { + when: !RemoteServiceModel.running && knownDeviceList.count > 0 + + PropertyChanges { + //: INFO ANDROID IOS + infoText.text: qsTr("This allows the paired devices to use this smartphone as a card reader.") + } + }, + State { + when: root.runningInPairingMode + + PropertyChanges { + infoText.Accessible.name: infoText.enterCodeString.arg(infoText.currentPin.split("").join(" ")).arg(Qt.application.name) + infoText.text: infoText.enterCodeString.arg(infoText.currentPin).arg(Qt.application.name) + } + }, + State { + when: RemoteServiceModel.running && !RemoteServiceModel.isPairing && knownDeviceList.count > 0 + + PropertyChanges { + infoText.text: "%1\n%2".arg( + //: INFO ANDROID IOS + qsTr("To do this, start a process on a paired device.")).arg( + //: INFO ANDROID IOS The remote service is active. Hint that both devices need to be connected to the same network. + qsTr("Both devices have to be on the same network (e.g. WiFi).")) + } } - } - GLink { - //: LABEL ANDROID IOS - Accessible.name: qsTr("Start pairing of a new device") - Layout.alignment: Qt.AlignLeft - Layout.topMargin: knownDevices.spacing - icon.source: "qrc:///images/material_add.svg" - padding: 0 - //: LABEL ANDROID IOS - text: qsTr("Pair new device") - tintIcon: true - visible: !RemoteServiceModel.isPairing && !RemoteServiceModel.running - - onClicked: RemoteServiceModel.setRunning(!RemoteServiceModel.running, !RemoteServiceModel.isPairing) - } + ] } } - GSpacer { - Layout.fillHeight: true - } - RemoteServiceWifiInfo { - id: wifiInfo - - Layout.fillWidth: true - Layout.topMargin: Style.dimens.pane_spacing - } - LocalNetworkInfo { - id: networkPermissionText + GText { + id: pairingCodeHeader - Layout.bottomMargin: Style.dimens.text_spacing - Layout.topMargin: Style.dimens.pane_spacing - visible: RemoteServiceModel.requiresLocalNetworkPermission - } - GProgressBar { - id: progressBar + Accessible.ignored: true + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter - Layout.fillWidth: true - Layout.topMargin: Style.dimens.pane_spacing - value: RemoteServiceModel.percentage - visible: progressText.visible + //: LABEL ANDROID IOS + text: qsTr("Pairing code:") + textStyle: Style.text.headline + visible: pairingCode.visible } GText { - id: progressText + id: pairingCode + readonly property string currentPin: RemoteServiceModel.psk + + Accessible.ignored: true Layout.alignment: Qt.AlignHCenter - Layout.topMargin: Style.dimens.text_spacing - text: RemoteServiceModel.displayText - visible: text !== "" + horizontalAlignment: Text.AlignHCenter + text: RemoteServiceModel.isPairing ? currentPin : "0000" + textStyle: Style.text.headline + visible: false } GButton { id: pairConnectButton Layout.alignment: Qt.AlignHCenter - Layout.topMargin: Style.dimens.pane_spacing enabled: !RemoteServiceModel.isStarting visible: text !== "" @@ -311,7 +253,7 @@ } }, State { - when: RemoteServiceModel.runnable && knownDeviceList.count > 0 && !RemoteServiceModel.isPairing && !RemoteServiceModel.running + when: RemoteServiceModel.runnable && knownDeviceList.count > 0 && root.notRunningNoPairingMode PropertyChanges { //: LABEL ANDROID IOS @@ -321,7 +263,7 @@ } }, State { - when: RemoteServiceModel.runnable && knownDeviceList.count < 1 && !RemoteServiceModel.isPairing + when: root.runnableNoPairedDevice PropertyChanges { //: LABEL ANDROID IOS @@ -355,4 +297,111 @@ onClicked: RemoteServiceModel.setRunning(true, !RemoteServiceModel.isPairing) } + MoreInformationLink { + id: paringCodeLink + + Layout.alignment: Qt.AlignCenter + //: LABEL ANDROID IOS + text: qsTr("Where do I enter the pairing code?") + visible: false + + onClicked: root.push(pairingCodeInfoView) + + Component { + id: pairingCodeInfoView + + PairingCodeInfoView { + text: paringCodeLink.text + + onNavActionClicked: root.pop() + } + } + } + NfcWorkflow { + id: nfcWorkflow + + Layout.fillHeight: true + Layout.fillWidth: true + Layout.topMargin: -root.spacing + bottomMargin: 0 + isRemoteWorkflow: true + leftMargin: 0 + rightMargin: 0 + topMargin: 0 + visible: root.nfcState === ApplicationModel.NfcState.UNAVAILABLE + + onShowRemoteServiceSettings: root.push(remoteServiceSettings) + + Component { + id: remoteServiceSettings + + RemoteServiceSettings { + Component.onCompleted: RemoteServiceModel.startDetection() + Component.onDestruction: RemoteServiceModel.stopDetection(true) + } + } + } + GOptionsContainer { + id: knownDevicesContainer + + containerPadding: Style.dimens.pane_padding + containerSpacing: Style.dimens.text_spacing + //: LABEL ANDROID IOS + title: qsTr("Paired Devices") + visible: RemoteServiceModel.runnable && knownDeviceList.count > 0 && !progressText.visible + + Repeater { + id: knownDeviceList + + model: RemoteServiceModel.allDevices + + delegate: DevicesListDelegate { + Layout.fillWidth: true + linkQualityVisible: false + titleColor: Style.color.textNormal.basic + } + } + GSeparator { + Layout.fillWidth: true + visible: addPairingLink.visible + } + GLink { + id: addPairingLink + + //: LABEL ANDROID IOS + Accessible.name: qsTr("Start pairing of a new device") + Layout.alignment: Qt.AlignLeft + horizontalPadding: 0 + icon.source: "qrc:///images/material_add.svg" + //: LABEL ANDROID IOS + text: qsTr("Pair new device") + tintIcon: true + verticalPadding: 0 + visible: root.notRunningNoPairingMode + + onClicked: RemoteServiceModel.setRunning(!RemoteServiceModel.running, !RemoteServiceModel.isPairing) + } + } + GSpacer { + Layout.fillHeight: true + } + LocalNetworkInfo { + id: networkPermissionText + + visible: RemoteServiceModel.requiresLocalNetworkPermission + } + GProgressBar { + //: LABEL ANDROID IOS Name of an progress indicator during the pairing process read by screen readers + Accessible.name: qsTr("Pairing progress") + Layout.fillWidth: true + value: RemoteServiceModel.percentage + visible: progressText.visible + } + GText { + id: progressText + + Layout.alignment: Qt.AlignHCenter + text: RemoteServiceModel.displayText + visible: text !== "" + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/+mobile/internal/DevicesListDelegate.qml ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/+mobile/internal/DevicesListDelegate.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/+mobile/internal/DevicesListDelegate.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/+mobile/internal/DevicesListDelegate.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,6 +14,7 @@ property alias description: descriptionText.text required property var deviceId + required property int index required property bool isLastAddedDevice required property bool isNetworkVisible required property bool isPaired @@ -22,6 +23,7 @@ property alias linkQualityVisible: linkQualityItem.visible required property string remoteDeviceName required property string remoteDeviceStatus + property alias titleColor: titleText.color signal activate(var pIsSupported, var pDeviceId) @@ -31,50 +33,60 @@ implicitHeight: content.implicitHeight implicitWidth: content.implicitWidth + Keys.onEnterPressed: clicked(null) + Keys.onReturnPressed: clicked(null) Keys.onSpacePressed: clicked(null) onClicked: activate(isSupported, deviceId) FocusFrame { marginFactor: 3 } - RowLayout { + ColumnLayout { id: content anchors.fill: parent - spacing: Style.dimens.groupbox_spacing + spacing: Style.dimens.text_spacing - ColumnLayout { + GSeparator { Layout.fillWidth: true - spacing: 2 - - GText { - id: titleText + visible: root.index > 0 + } + RowLayout { + spacing: Style.dimens.groupbox_spacing - Accessible.ignored: true - elide: Text.ElideRight - font.bold: root.isLastAddedDevice - maximumLineCount: 1 - text: root.remoteDeviceName + (root.isSupported ? "" : (" (" + root.remoteDeviceStatus + ")")) - textFormat: Text.PlainText - textStyle: Style.text.subline + ColumnLayout { + Layout.fillWidth: true + spacing: 2 + + GText { + id: titleText + + Accessible.ignored: true + elide: Text.ElideRight + font.weight: root.isLastAddedDevice ? Style.font.bold : Style.font.normal + maximumLineCount: 1 + text: root.remoteDeviceName + (root.isSupported ? "" : (" (" + root.remoteDeviceStatus + ")")) + textFormat: Text.PlainText + textStyle: Style.text.subline + } + GText { + id: descriptionText + + Accessible.ignored: true + elide: Text.ElideRight + maximumLineCount: 1 + visible: text !== "" + } } - GText { - id: descriptionText - - Accessible.ignored: true - elide: Text.ElideRight - maximumLineCount: 1 - visible: text !== "" + GSpacer { + Layout.fillWidth: true } - } - GSpacer { - Layout.fillWidth: true - } - LinkQualityAnimation { - id: linkQualityItem + LinkQualityAnimation { + id: linkQualityItem - inactive: !root.isNetworkVisible && root.isPaired - percent: root.linkQualityInPercent + inactive: !root.isNetworkVisible && root.isPaired + percent: root.linkQualityInPercent + } } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/+mobile/internal/PairingCodeInfoView.qml ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/+mobile/internal/PairingCodeInfoView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/+mobile/internal/PairingCodeInfoView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/+mobile/internal/PairingCodeInfoView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -15,7 +15,7 @@ id: root readonly property string currentPin: RemoteServiceModel.psk - property alias text: descriptionContainer.title + property alias text: headline.text signal navActionClicked @@ -32,49 +32,42 @@ onClicked: root.navActionClicked() } - GOptionsContainer { - id: descriptionContainer + Heading { + id: headline - containerPadding: Style.dimens.pane_padding - - ColumnLayout { - spacing: Style.dimens.pane_spacing - width: parent.width - - TintableIcon { - Layout.alignment: Qt.AlignHCenter - Layout.maximumWidth: parent.width - Layout.preferredHeight: Style.dimens.medium_icon_size - source: "qrc:///images/phone_to_pc.svg" - sourceSize.height: Style.dimens.medium_icon_size - tintColor: Style.color.image - } - Repeater { - model: [ - //: LABEL ANDROID IOS Assistance text for pairing new devices. Step 1 of 4 - qsTr("Open %1 on your %2other device%3.").arg(Qt.application.name).arg("").arg(""), - //: LABEL ANDROID IOS Assistance text for pairing new devices. Step 2 of 4. %1 and %2 are surrounding tags for bold font. - qsTr("On that device go to %1Settings%2 and then %1Smartphone as card reader%2 resp. %1Manage pairings%2.").arg("").arg(""), - //: LABEL ANDROID IOS Assistance text for pairing new devices. Step 3 of 4 - qsTr("Choose this smartphone in the list to pair it."), - //: LABEL ANDROID IOS Provide pairing code. Step 4 of 4 - qsTr("Enter the pairing code \"%1\".").arg(root.currentPin)] - - GText { - required property int index - required property string modelData - - Accessible.name: ApplicationModel.stripHtmlTags(text) - Layout.alignment: Qt.AlignTop - text: (index + 1) + ". " + modelData - } - } - } } - GSpacer { - Layout.fillHeight: true + TintableIcon { + Layout.alignment: Qt.AlignHCenter + Layout.maximumWidth: parent.width + Layout.preferredHeight: Style.dimens.medium_icon_size + source: "qrc:///images/phone_to_pc.svg" + sourceSize.height: Style.dimens.medium_icon_size + tintColor: Style.color.image } - RemoteServiceWifiInfo { - Layout.fillWidth: true + Subheading { + //: LABEL ANDROID IOS + text: qsTr("Please follow these steps:") + } + Repeater { + model: [ + //: LABEL ANDROID IOS Assistance text for pairing new devices. Step 1 of 5 + qsTr("Open %1 on your %2other device%3.").arg(Qt.application.name).arg("").arg(""), + //: LABEL ANDROID IOS Assistance text for pairing new devices. Step 2 of 5. %1 and %2 are surrounding tags for bold font. + qsTr("Make sure that both devices are on the %1same network%2 (e.g. WiFi).").arg("").arg(""), + //: LABEL ANDROID IOS Assistance text for pairing new devices. Step 3 of 5. %1 and %2 are surrounding tags for bold font. + qsTr("On your other device, go to %1Settings%2 and then %1Smartphone as card reader%2 resp. %1Manage pairings%2.").arg("").arg(""), + //: LABEL ANDROID IOS Assistance text for pairing new devices. Step 4 of 5 + qsTr("Choose this smartphone in the list to pair it."), + //: LABEL ANDROID IOS Provide pairing code. Step 5 of 5 + qsTr("Enter the pairing code \"%1\".").arg(root.currentPin)] + + GText { + required property int index + required property string modelData + + Accessible.name: ApplicationModel.stripHtmlTags(text) + Layout.alignment: Qt.AlignTop + text: (index + 1) + ". " + modelData + } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/+mobile/internal/RemoteServiceWifiInfo.qml ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/+mobile/internal/RemoteServiceWifiInfo.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/+mobile/internal/RemoteServiceWifiInfo.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/+mobile/internal/RemoteServiceWifiInfo.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2023-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick.Layouts - -import Governikus.Global -import Governikus.Style - -RowLayout { - spacing: Style.dimens.text_spacing - - TintableIcon { - source: "qrc:/images/info.svg" - sourceSize.width: Style.dimens.small_icon_size - tintColor: infoText.color - } - GText { - id: infoText - - color: Style.color.textSubline.basic - - //: INFO ANDROID IOS The remote service is active. Hint that both devices need to be connected to the same network. - text: qsTr("Both devices have to be on the same network (e.g. WiFi).") - } - GSpacer { - Layout.fillWidth: true - } -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/PairingFailedView.qml ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/PairingFailedView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/PairingFailedView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/PairingFailedView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -12,7 +12,7 @@ required property string errorMessage animationSymbol: Symbol.Type.ERROR - animationType: AnimationLoader.SAC_CONNECTION + animationType: AnimationLoader.Type.SAC_CONNECTION //: LABEL ALL_PLATFORMS header: qsTr("Pairing failed") //: LABEL ALL_PLATFORMS diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/PairingSuccessView.qml ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/PairingSuccessView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/RemoteServiceView/PairingSuccessView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/RemoteServiceView/PairingSuccessView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -11,7 +11,7 @@ required property string deviceName animationSymbol: Symbol.Type.CHECK - animationType: AnimationLoader.SAC_CONNECTION + animationType: AnimationLoader.Type.SAC_CONNECTION //: LABEL ALL_PLATFORMS header: qsTr("Pairing successful") //: LABEL ALL_PLATFORMS diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/+desktop/ReaderFoundConfirmation.qml ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/+desktop/ReaderFoundConfirmation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/+desktop/ReaderFoundConfirmation.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/+desktop/ReaderFoundConfirmation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick + +import Governikus.Animations +import Governikus.ResultView +import Governikus.TitleBar + +ResultView { + id: root + + enum ReaderType { + REMOTE, + PCSC + } + + required property int type + + animationSymbol: Symbol.Type.CHECK + animationType: switch (type) { + case ReaderFoundConfirmation.ReaderType.PCSC: + return AnimationLoader.Type.WAIT_FOR_READER; + case ReaderFoundConfirmation.ReaderType.REMOTE: + return AnimationLoader.Type.WAIT_FOR_SAC; + } + + //: LABEL DESKTOP + buttonText: qsTr("Continue") + text: switch (type) { + case ReaderFoundConfirmation.ReaderType.PCSC: + //: LABEL DESKTOP + return qsTr("Found new USB card reader that is suitable for the ID card. The workflow may now be continued."); + case ReaderFoundConfirmation.ReaderType.REMOTE: + //: LABEL DESKTOP + return qsTr("Found new smartphone as card reader that is suitable for the ID card. The workflow may now be continued."); + } + title: root.title + + titleBarSettings: TitleBarSettings { + navigationAction: NavigationAction.Action.Close + + onNavigationActionClicked: root.leaveView() + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/+desktop/ResultView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/+desktop/ResultView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/+desktop/ResultView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/+desktop/ResultView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -23,9 +23,11 @@ property alias buttonText: button.text default property alias data: resultContent.data property alias header: resultHeader.text + property string hintButtonLink property alias hintButtonText: hintItem.buttonText property alias hintText: hintItem.text property alias hintTitle: hintItem.title + property string linkToOpen property alias mailButtonVisible: mailButton.visible property string popupText property string popupTitle @@ -46,16 +48,9 @@ spacing: Style.dimens.pane_spacing - Keys.onEnterPressed: button.clicked() - Keys.onEscapePressed: button.clicked() - Keys.onReturnPressed: button.clicked() - - GText { + Heading { id: resultHeader - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - textStyle: Style.text.headline visible: text !== "" } WorkflowAnimationLoader { @@ -64,12 +59,11 @@ Layout.alignment: Qt.AlignHCenter animated: false } - GText { + Subheading { id: subheader Layout.alignment: Qt.AlignHCenter horizontalAlignment: Text.AlignHCenter - textStyle: Style.text.subline visible: text !== "" } GText { @@ -110,8 +104,7 @@ tintIcon: true onClicked: { - LogModel.setLogFile(0); - let filenameSuggestion = LogModel.createLogFileName(LogModel.getCurrentLogFileDate()); + let filenameSuggestion = logModel.createLogFileName(); fileDialog.selectFile(filenameSuggestion); } @@ -125,7 +118,11 @@ //: LABEL DESKTOP title: qsTr("Save log") - onAccepted: LogModel.saveCurrentLogFile(file) + onAccepted: logModel.saveLogFile(selectedFile, true) + } + LogModel { + id: logModel + } } GButton { @@ -164,6 +161,7 @@ id: hintItem Layout.fillWidth: true + linkToOpen: root.hintButtonLink //: LABEL DESKTOP title: qsTr("Hint") visible: text !== "" @@ -173,9 +171,14 @@ GButton { id: button + readonly property bool hasLink: root.linkToOpen !== "" + + Accessible.description: hasLink ? Utils.platformAgnosticLinkOpenText(root.linkToOpen, Accessible.name) : "" + Accessible.role: hasLink ? Accessible.Link : Accessible.Button Layout.alignment: Qt.AlignHCenter Layout.preferredHeight: height Layout.preferredWidth: width + enabledTooltipText: hasLink ? root.linkToOpen : "" text: qsTr("OK") tintIcon: true visible: text !== "" diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/+mobile/ResultErrorView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/+mobile/ResultErrorView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/+mobile/ResultErrorView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/+mobile/ResultErrorView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -38,9 +38,8 @@ visible: root.hasErrorDetails GText { - font.weight: Font.Bold - //: LABEL ANDROID IOS - text: "%1 %2".arg(qsTr("Error code:")).arg(root.errorCode) + font.weight: Style.font.bold + text: root.errorCode } GText { id: textErrorDescription diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/+mobile/ResultView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/+mobile/ResultView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/+mobile/ResultView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/+mobile/ResultView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -22,9 +22,11 @@ property alias buttonText: buttonContinue.text default property alias children: layout.data property alias header: paneTitle.text + property string hintButtonLink property alias hintButtonText: hintItem.buttonText property alias hintText: hintItem.text property alias hintTitle: hintItem.title + property string linkToOpen property alias subheader: subheader.text property alias text: resultText.text property alias textFormat: resultText.textFormat @@ -62,10 +64,9 @@ Layout.maximumWidth: Number.POSITIVE_INFINITY spacing: Style.dimens.pane_spacing - GText { + Subheading { id: subheader - textStyle: Style.text.subline visible: text !== "" } GText { @@ -81,6 +82,7 @@ id: hintItem Layout.fillWidth: true + linkToOpen: root.hintButtonLink //: LABEL ANDROID IOS title: qsTr("Hint") visible: text !== "" @@ -90,6 +92,10 @@ GButton { id: buttonContinue + readonly property bool hasLink: root.linkToOpen !== "" + + Accessible.description: hasLink ? Utils.platformAgnosticLinkOpenText(root.linkToOpen, Accessible.name) : "" + Accessible.role: hasLink ? Accessible.Link : Accessible.Button Layout.alignment: Qt.AlignHCenter //: LABEL ANDROID IOS diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/InputErrorView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/InputErrorView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/InputErrorView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/InputErrorView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -26,10 +26,6 @@ spacing: Style.dimens.pane_spacing - Keys.onEnterPressed: continueClicked() - Keys.onEscapePressed: continueClicked() - Keys.onReturnPressed: continueClicked() - MultiInfoData { id: infoData @@ -58,27 +54,25 @@ case CardReturnCode.INVALID_PIN: case CardReturnCode.INVALID_PIN_2: case CardReturnCode.INVALID_PIN_3: - return root.isTransportPin ? AnimationLoader.TRANSPORT_PIN : AnimationLoader.PIN; + return root.isTransportPin ? AnimationLoader.Type.TRANSPORT_PIN : AnimationLoader.Type.PIN; case CardReturnCode.INVALID_CAN: - return AnimationLoader.CAN; + return AnimationLoader.Type.CAN; case CardReturnCode.INVALID_PUK: - return AnimationLoader.PUK; + return AnimationLoader.Type.PUK; } switch (root.passwordType) { case NumberModel.PasswordType.NEW_PIN_CONFIRMATION: case NumberModel.PasswordType.NEW_PIN: case NumberModel.PasswordType.NEW_SMART_PIN: case NumberModel.PasswordType.NEW_SMART_PIN_CONFIRMATION: - return AnimationLoader.NEW_PIN; + return AnimationLoader.Type.NEW_PIN; } - return AnimationLoader.NONE; + return AnimationLoader.Type.NONE; } } - GText { + Heading { id: title - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter text: { switch (root.returnCode) { case CardReturnCode.INVALID_CAN: @@ -110,7 +104,6 @@ } return ""; } - textStyle: Style.text.headline } GText { Layout.alignment: Qt.AlignHCenter diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/InputSuccessView.qml ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/InputSuccessView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/ResultView/InputSuccessView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/ResultView/InputSuccessView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -21,15 +21,8 @@ spacing: Style.dimens.pane_spacing - Keys.onEnterPressed: continueClicked() - Keys.onEscapePressed: continueClicked() - Keys.onReturnPressed: continueClicked() - - GText { - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter + Heading { text: header.text - textStyle: Style.text.headline visible: subHeader.visible } AnimationLoader { @@ -42,22 +35,20 @@ type: { switch (root.passwordType) { case NumberModel.PasswordType.CAN: - return AnimationLoader.CAN; + return AnimationLoader.Type.CAN; case NumberModel.PasswordType.PUK: - return AnimationLoader.PUK; + return AnimationLoader.Type.PUK; case NumberModel.PasswordType.TRANSPORT_PIN: - return AnimationLoader.TRANSPORT_PIN; + return AnimationLoader.Type.TRANSPORT_PIN; default: - return AnimationLoader.NONE; + return AnimationLoader.Type.NONE; } } } - GText { + Heading { id: header - Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: Style.dimens.pane_spacing - horizontalAlignment: Text.AlignHCenter text: { switch (root.passwordType) { case NumberModel.PasswordType.CAN: @@ -73,15 +64,13 @@ return ""; } } - textStyle: Style.text.headline visible: !subHeader.visible } - GText { + Subheading { id: subHeader //: LABEL ALL_PLATFORMS text: qsTr("Now set your personal ID card PIN") - textStyle: Style.text.subline visible: root.passwordType === NumberModel.PasswordType.TRANSPORT_PIN } GText { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SelfAuthenticationView/+desktop/SelfAuthenticationView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SelfAuthenticationView/+desktop/SelfAuthenticationView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SelfAuthenticationView/+desktop/SelfAuthenticationView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SelfAuthenticationView/+desktop/SelfAuthenticationView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -13,12 +13,10 @@ disagreeButtonText: "" titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } - Keys.onEnterPressed: root.startSelfAuth() - Keys.onReturnPressed: root.startSelfAuth() onStartSelfAuth: SelfAuthModel.startWorkflow() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/CardReaderView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/CardReaderView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/CardReaderView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/CardReaderView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -15,14 +15,14 @@ property bool enablePaneOptic: true readonly property bool hasConnectedReader: readerRepeater.count > 0 - readonly property string hintTextBase: { - //: LABEL DESKTOP - qsTr("After connecting a new card reader it may take a few seconds to recognize the driver.") + "
" + - //: LABEL DESKTOP - qsTr("It may be necessary to restart your system after installing the driver."); - } + readonly property string hintTextBase: "%1
%2".arg( + //: LABEL DESKTOP + qsTr("After connecting a new card reader it may take a few seconds to recognize the driver.")).arg( + //: LABEL DESKTOP + qsTr("It may be necessary to restart your system after installing the driver.")) property alias showHint: hintAndDateText.visible + Accessible.ignored: true color: root.enablePaneOptic ? Style.color.pane.background.basic : Style.color.transparent contentPadding: root.enablePaneOptic ? Style.dimens.pane_padding : 0 layer.enabled: root.enablePaneOptic @@ -39,6 +39,7 @@ //: LABEL DESKTOP Accessible.name: qsTr("List of connected card readers.") Accessible.role: Accessible.List + Layout.topMargin: -root.contentSpacing spacing: Style.dimens.pane_spacing visible: root.hasConnectedReader @@ -103,11 +104,8 @@ Layout.alignment: Qt.AlignVCenter color: Style.color.textSubline.basic - text: { - root.hintTextBase + " " + - //: LABEL DESKTOP - qsTr("Only connected card readers are shown here.") + " " + ReaderModel.lastUpdatedInformation; - } + //: LABEL DESKTOP + text: root.hintTextBase + " " + qsTr("Only connected card readers are shown here.") + " " + ReaderModel.lastUpdatedInformation verticalAlignment: Text.AlignBottom } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/ConnectSacView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/ConnectSacView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/ConnectSacView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/ConnectSacView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -26,7 +26,7 @@ title: qsTr("Pairing") titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Cancel + navigationAction: NavigationAction.Action.Cancel onNavigationActionClicked: root.pairingFailed() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/DarkModeButtons.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/DarkModeButtons.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/DarkModeButtons.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/DarkModeButtons.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,86 +1,38 @@ /** * Copyright (c) 2023-2025 Governikus GmbH & Co. KG, Germany */ -import QtQuick + +pragma ComponentBehavior: Bound + import QtQuick.Layouts + import Governikus.Global import Governikus.Type -ColumnLayout { +RowLayout { id: root - readonly property var checkedButton: system.checked ? system : dark.checked ? dark : light - readonly property string selectedIconPath: checkedButton.icon.source - readonly property string selectedText: checkedButton.text - - signal buttonClicked - - function onChanged(checked, mode) { - if (!checked || SettingsModel.userDarkMode === mode) + function onChanged(pChecked, pMode) { + if (!pChecked || SettingsModel.userDarkMode === pMode) return; - SettingsModel.userDarkMode = mode; - root.buttonClicked(); + SettingsModel.userDarkMode = pMode; } spacing: 0 - Component.onCompleted: { - if (!UiPluginModel.osDarkModeSupported) - system.visible = false; - } - - GRadioButton { - id: system - - readonly property var mode: SettingsModel.ModeOption.AUTO - - //: LABEL ALL_PLATFORMS - Accessible.description: qsTr("Set the app appearance to system mode") - Layout.fillWidth: true - checked: SettingsModel.userDarkMode === mode - icon.source: "qrc:///images/appearance_system_mode.svg" - //: LABEL ALL_PLATFORMS - text: qsTr("System") - tintIcon: true - - onCheckedChanged: root.onChanged(checked, mode) - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) - } - GRadioButton { - id: dark - - readonly property var mode: SettingsModel.ModeOption.ON - - //: LABEL ALL_PLATFORMS - Accessible.description: qsTr("Set the app appearance to dark mode") - Layout.fillWidth: true - checked: SettingsModel.userDarkMode === mode - icon.source: "qrc:///images/appearance_dark_mode.svg" - //: LABEL ALL_PLATFORMS - text: qsTr("Dark") - tintIcon: true - - onCheckedChanged: root.onChanged(checked, mode) - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) - } - GRadioButton { - id: light - - readonly property var mode: SettingsModel.ModeOption.OFF + GRepeater { + id: repeater - //: LABEL ALL_PLATFORMS - Accessible.description: qsTr("Set the app appearance to light mode") - Layout.fillWidth: true - checked: SettingsModel.userDarkMode === mode - icon.source: "qrc:///images/appearance_light_mode.svg" - //: LABEL ALL_PLATFORMS - text: qsTr("Light") - tintIcon: true - - onCheckedChanged: root.onChanged(checked, mode) - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) + delegate: GRadioButton { + required property int index + required property int value + + checked: SettingsModel.userDarkMode === value + position: getPosition(index, repeater.count) + + onToggled: SettingsModel.userDarkMode = value + } + model: DarkModeButtonData { + } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/LanguageButtons.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/LanguageButtons.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/LanguageButtons.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/LanguageButtons.qml 2025-10-30 10:10:48.000000000 +0000 @@ -7,24 +7,25 @@ import QtQuick.Layouts import Governikus.Global -import Governikus.Style +import Governikus.Type -GridLayout { +RowLayout { id: root - signal buttonClicked - - columnSpacing: Style.dimens.pane_spacing - rowSpacing: Style.dimens.pane_spacing + spacing: 0 GRepeater { id: repeater - delegate: LocationButton { - Layout.fillWidth: true - Layout.preferredWidth: Style.is_layout_desktop ? repeater.maxItemWidth : -1 + delegate: GRadioButton { + required property int index + required property string value + + checked: SettingsModel.language === value + position: getPosition(index, repeater.count) + tintIcon: false - onClicked: root.buttonClicked() + onToggled: SettingsModel.language = value } model: LanguageButtonData { } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/RemoteReaderDelegate.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/RemoteReaderDelegate.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/RemoteReaderDelegate.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/RemoteReaderDelegate.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,15 +1,13 @@ /** * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany */ + import QtQuick -import QtQuick.Controls import QtQuick.Layouts import Governikus.Animations import Governikus.Global import Governikus.Style -import Governikus.View -import Governikus.Type RoundedRectangle { id: root @@ -27,33 +25,13 @@ signal pairDevice(var pDeviceId) signal unpairDevice(var pDeviceId) - Accessible.name: { - //: INFO DESKTOP Name and status of remote device. %1 is replaced with the name, %2 with the status - let msg = qsTr("Smartphone named \"%1\". %2. ").arg(remoteDeviceName).arg(subtext.text); - if (isPaired && !isPairing) { - //: INFO DESKTOP Text for activation action if the device is paired. - return msg + qsTr("Press space to unpair the smartphone \"%1\".").arg(remoteDeviceName); - } - //: INFO DESKTOP Text for activation action if the device is unpaired. - return msg + qsTr("Press space to pair the smartphone \"%1\".").arg(remoteDeviceName); - } - Accessible.role: Accessible.Button - activeFocusOnTab: ApplicationModel.isScreenReaderRunning + //: INFO DESKTOP Name of remote device. %1 is replaced with the name. + Accessible.name: qsTr("Smartphone named \"%1\".").arg(remoteDeviceName) + Accessible.role: Accessible.Grouping color: Style.color.paneSublevel.background.basic implicitHeight: rowLayout.implicitHeight + 2 * rowLayout.anchors.margins implicitWidth: rowLayout.implicitWidth + 2 * rowLayout.anchors.margins - Keys.onSpacePressed: { - if (!ApplicationModel.isScreenReaderRunning) { - return; - } - if (hasCustomContent) { - return; - } - - (isPaired && !isPairing) ? unpairDevice(deviceId) : pairDevice(deviceId); - } - RowLayout { id: rowLayout @@ -71,7 +49,7 @@ Layout.fillWidth: true GText { - activeFocusOnTab: false + Accessible.ignored: true elide: Text.ElideRight maximumLineCount: 1 text: root.remoteDeviceName @@ -80,54 +58,30 @@ width: parent.width } GText { - id: subtext - - activeFocusOnTab: false + //: INFO DESKTOP Status of remote device. %1 is replaced with the status. + Accessible.name: qsTr("Status: \"%1\".").arg(text) text: root.remoteDeviceStatus visible: text !== "" width: parent.width } } GButton { - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - activeFocusOnTab: !ApplicationModel.isScreenReaderRunning - //: LABEL DESKTOP - text: qsTr("Pair") - visible: root.isPairing && !root.hasCustomContent + id: delegate - onClicked: root.pairDevice(root.deviceId) - } - TintableIcon { - id: removeIcon + readonly property bool shouldPair: root.isPairing && !root.hasCustomContent + readonly property bool shouldUnpair: root.isPaired && !root.isPairing && !root.hasCustomContent + //: LABEL DESKTOP Text of pairing button, %1 will be Pair/Unpair and %2 is replaced with device name + Accessible.name: qsTr("%1 device \"%2\"").arg(text).arg(root.remoteDeviceName) Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - activeFocusOnTab: !ApplicationModel.isScreenReaderRunning - source: "qrc:///images/trash_icon.svg" - sourceSize.height: Style.dimens.icon_size - tintColor: Style.color.image - visible: root.isPaired && !root.isPairing && !root.hasCustomContent - - Keys.onSpacePressed: root.unpairDevice(root.deviceId) - - MouseArea { - id: trashMouse - - ToolTip.delay: Style.toolTipDelay - ToolTip.text: qsTr("Remove remote device") - ToolTip.visible: trashMouse.containsMouse - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true + icon.source: shouldUnpair ? "qrc:///images/trash_icon.svg" : "" + //: LABEL DESKTOP + text: shouldPair ? qsTr("Pair") : + //: LABEL DESKTOP + shouldUnpair ? qsTr("Unpair") : "" + tintIcon: true - onClicked: root.unpairDevice(root.deviceId) - } - FocusFrame { - } - GSpacer { - height: removeIcon.height - visible: !trashMouse.visible && root.isPairing - width: removeIcon.width - } + onClicked: shouldPair ? root.pairDevice(root.deviceId) : shouldUnpair ? root.unpairDevice(root.deviceId) : {} } Loader { id: customContentLoader diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/RemoteReaderView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/RemoteReaderView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/RemoteReaderView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/RemoteReaderView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -18,6 +18,7 @@ signal pairDevice(var pDeviceId) signal showNoSacFoundInfo + Accessible.ignored: !ApplicationModel.screenReaderRunning && (Qt.platform.os === "osx" || Qt.platform.os === "ios") implicitHeight: column.implicitHeight implicitWidth: column.implicitWidth @@ -27,11 +28,12 @@ Column { id: column + Accessible.ignored: root.Accessible.ignored anchors.fill: parent spacing: Style.dimens.pane_spacing GPane { - spacing: Style.dimens.pane_spacing + Accessible.ignored: root.Accessible.ignored //: LABEL DESKTOP title: qsTr("Paired devices") visible: availablePairedDevices.count > 0 @@ -55,8 +57,7 @@ } } GPane { - spacing: Style.dimens.pane_spacing - + Accessible.ignored: root.Accessible.ignored //: LABEL DESKTOP title: qsTr("Last connected") visible: unavailablePairedDevices.count > 0 @@ -80,7 +81,7 @@ } } GPane { - spacing: Style.dimens.pane_spacing + Accessible.ignored: root.Accessible.ignored //: LABEL DESKTOP title: qsTr("Add pairing") @@ -103,6 +104,7 @@ } } PairingProcessInfo { + Accessible.ignored: root.Accessible.ignored Layout.fillWidth: true visible: availableDevices.count === 0 diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/SettingsView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/SettingsView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/SettingsView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/SettingsView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -20,22 +20,15 @@ title: qsTr("Settings") titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } Keys.onPressed: event => { - tabbedPane.handleKeyPress(event.key); + tabbedPane.handleKeyPress(event); } - Connections { - function onFireAppUpdateDataChanged() { - root.push(updateView); - } - - target: SettingsModel - } TabbedPane { id: tabbedPane @@ -64,6 +57,7 @@ contentObjectModel: ObjectModel { Component { GeneralSettings { + onShowUpdateRequested: root.push(updateView) } } Component { @@ -82,10 +76,10 @@ progress: root.progress infoContent: MultiInfoData { - contentType: MultiInfoData.NO_SAC_FOUND + contentType: MultiInfoData.Type.NO_SAC_FOUND } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/TabbedReaderView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/TabbedReaderView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/TabbedReaderView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/TabbedReaderView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -7,7 +7,6 @@ import QtQuick import QtQml.Models -import Governikus.Animations import Governikus.Global import Governikus.MultiInfoView import Governikus.ResultView @@ -19,52 +18,47 @@ SectionPage { id: root - property int lastReaderCount: 0 - //: LABEL DESKTOP - title: qsTr("Card Readers") + title: qsTr("Card readers") titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.leaveView() } - Component.onCompleted: lastReaderCount = ApplicationModel.availablePcscReader Keys.onPressed: event => { - tabbedPane.handleKeyPress(event.key); + tabbedPane.handleKeyPress(event); } - Connections { - function onFireAvailableReaderChanged() { - if (ApplicationModel.availablePcscReader > root.lastReaderCount) { - root.push(pcscReaderFound); + ReaderDetection { + onNewPcscReaderDetected: { + if (ApplicationModel.screenReaderRunning) { + root.push(readerFoundConfirmation, { + type: ReaderFoundConfirmation.ReaderType.PCSC + }); + } else { + root.leaveView(); + } + } + onNewRemoteReaderDetected: { + if (ApplicationModel.screenReaderRunning) { + root.push(readerFoundConfirmation, { + type: ReaderFoundConfirmation.ReaderType.REMOTE + }); + } else { + root.leaveView(); } - root.lastReaderCount = ApplicationModel.availablePcscReader; } - - target: ApplicationModel } Component { - id: pcscReaderFound + id: readerFoundConfirmation - ResultView { - animationSymbol: Symbol.Type.CHECK - animationType: AnimationLoader.Type.WAIT_FOR_READER - text: qsTr("Found new USB card reader that is suitable for the ID card. The workflow may now be continued.") + ReaderFoundConfirmation { title: root.title - titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back - - onNavigationActionClicked: { - root.pop(); - root.leaveView(); - } - } - onLeaveView: { - pop(); + root.pop(); root.leaveView(); } } @@ -77,9 +71,6 @@ contentObjectModel: ObjectModel { Component { RemoteReaderView { - height: Math.max(implicitHeight, tabbedPane.availableHeight) - width: parent.width - onPairDevice: pDeviceId => { if (RemoteServiceModel.rememberServer(pDeviceId)) { root.push(connectSacView); @@ -94,10 +85,10 @@ progress: root.progress infoContent: MultiInfoData { - contentType: MultiInfoData.NO_SAC_FOUND + contentType: MultiInfoData.Type.NO_SAC_FOUND } titleBarSettings: TitleBarSettings { - navigationAction: NavigationAction.Back + navigationAction: NavigationAction.Action.Back onNavigationActionClicked: root.pop() } @@ -118,8 +109,6 @@ } Component { CardReaderView { - height: Math.max(implicitHeight, tabbedPane.availableHeight) - width: parent.width } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/CardReaderDelegate.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/CardReaderDelegate.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/CardReaderDelegate.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/CardReaderDelegate.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,7 +14,7 @@ RoundedRectangle { id: root - property int iconHeight: UiPluginModel.scaleFactor * 175 + property int iconHeight: UiPluginModel.scaleFactor * 105 required property string readerHTMLDescription required property url readerImagePath required property bool readerInstalled @@ -25,11 +25,13 @@ //: INFO DESKTOP Text read by screen reader if the text contains a web link to a card reader driver which may be opened. Accessible.name: readerName + ". " + ApplicationModel.stripHtmlTags(readerHTMLDescription) + ". " + (textDescription.hasLink ? qsTr("Press space to open link.") : "") Accessible.role: textDescription.hasLink ? Accessible.Button : Accessible.ListItem - activeFocusOnTab: true + activeFocusOnTab: textDescription.hasLink color: Style.color.paneSublevel.background.basic implicitHeight: rowLayout.implicitHeight implicitWidth: rowLayout.implicitWidth + Keys.onEnterPressed: textDescription.tryActivateLink() + Keys.onReturnPressed: textDescription.tryActivateLink() Keys.onSpacePressed: textDescription.tryActivateLink() FocusFrame { @@ -73,7 +75,6 @@ GText { Accessible.ignored: true Layout.alignment: Qt.AlignLeft - activeFocusOnTab: false clip: true text: root.readerName textStyle: Style.text.headline @@ -83,7 +84,6 @@ Accessible.ignored: true Layout.alignment: Qt.AlignLeft - activeFocusOnTab: false text: root.readerHTMLDescription visible: text !== "" } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/DebugSettings.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/DebugSettings.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/DebugSettings.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/DebugSettings.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,7 +14,6 @@ GPane { Layout.fillWidth: true contentPadding: 0 - spacing: Style.dimens.pane_spacing //: LABEL DESKTOP title: qsTr("Create dummy entries") @@ -29,7 +28,7 @@ text: qsTr("Logfile") onClicked: { - LogModel.saveDummyLogFile(); + LogFilesModel.saveDummyLogFile(); ApplicationModel.showFeedback("Created new logfile."); } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/DeveloperSettings.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/DeveloperSettings.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/DeveloperSettings.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/DeveloperSettings.qml 2025-10-30 10:10:48.000000000 +0000 @@ -2,6 +2,7 @@ * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany */ +import QtQuick import QtQuick.Layouts import Governikus.Global @@ -16,7 +17,7 @@ GPane { Layout.fillWidth: true contentPadding: 0 - spacing: 0 + contentSpacing: 0 //: LABEL DESKTOP title: qsTr("Developer options") @@ -63,10 +64,25 @@ onFocusChanged: if (focus) Utils.positionViewAtItem(this) } + GSwitch { + Layout.fillWidth: true + checked: SettingsModel.showInAppNotifications + + //: LABEL DESKTOP Only visible when the user activates the developer mode in the settings. + description: SettingsModel.developerMode ? qsTr("Using the developer mode forces the notifications to be enabled.") : "" + drawBottomCorners: true + enabled: !SettingsModel.developerMode + + //: LABEL DESKTOP + text: qsTr("Show notifications inside of %1").arg(Qt.application.name) + + onCheckedChanged: SettingsModel.showInAppNotifications = checked + onFocusChanged: if (focus) + Utils.positionViewAtItem(this) + } } GPane { Layout.fillWidth: true - spacing: Style.dimens.pane_spacing //: LABEL DESKTOP title: qsTr("Custom config.json") @@ -93,15 +109,15 @@ GFileDialog { id: fileDialog + currentFolder: ApplicationModel.customConfigPath defaultSuffix: "json" - folder: ApplicationModel.customConfigPath //: LABEL DESKTOP nameFilters: qsTr("JSON config (*.json)") //: LABEL DESKTOP title: qsTr("Save config.json") - onAccepted: ApplicationModel.saveEmbeddedConfig(file) + onAccepted: ApplicationModel.saveEmbeddedConfig(selectedFile) } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/GRadioButton.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/GRadioButton.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/GRadioButton.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/GRadioButton.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany + */ + +pragma ComponentBehavior: Bound + +import QtQuick + +import Governikus.Global +import Governikus.Style + +GAbstractButton { + id: root + + enum Position { + Left, + Middle, + Right + } + + required property string desc + required property url img + required property string name + required property int position + property alias tintIcon: icon.tintEnabled + + function getPosition(index: int, buttonCount: int): int { + if (index === 0) + return GRadioButton.Position.Left; + else if (index === buttonCount - 1) + return GRadioButton.Position.Right; + else + return GRadioButton.Position.Middle; + } + + Accessible.description: desc + Accessible.name: name + Accessible.role: Accessible.RadioButton + autoExclusive: true + bottomPadding: backgroundRectangle.radius + checkable: true + icon.source: img + leftPadding: root.position === GRadioButton.Position.Left ? backgroundRectangle.radius : backgroundRectangle.radius / 2 + rightPadding: root.position === GRadioButton.Position.Right ? backgroundRectangle.radius : backgroundRectangle.radius / 2 + topPadding: backgroundRectangle.radius + z: activeFocus ? 1 : 0 + + background: Rectangle { + id: backgroundRectangle + + border.color: colors.controlBorder + border.width: Style.dimens.border_width + bottomLeftRadius: root.position === GRadioButton.Position.Left ? radius : 0 + bottomRightRadius: root.position === GRadioButton.Position.Right ? radius : 0 + color: colors.controlBackground + radius: Style.dimens.pane_radius + topLeftRadius: root.position === GRadioButton.Position.Left ? radius : 0 + topRightRadius: root.position === GRadioButton.Position.Right ? radius : 0 + + Rectangle { + // overdraw left border to hide it + anchors.left: backgroundRectangle.left + anchors.top: backgroundRectangle.top + anchors.topMargin: border.width + color: colors.controlBackground + height: backgroundRectangle.height - 2 * backgroundRectangle.border.width + visible: root.position !== GRadioButton.Position.Left + width: backgroundRectangle.border.width + } + Rectangle { + // overdraw right border of previous button when this button is checked to hide it + anchors.right: backgroundRectangle.left + anchors.top: backgroundRectangle.top + color: colors.controlBorder + height: backgroundRectangle.height + visible: root.position !== GRadioButton.Position.Left && root.checked + width: backgroundRectangle.border.width + } + GRadioButtonFocusFrame { + bottomLeftRadius: backgroundRectangle.bottomLeftRadius + bottomRightRadius: backgroundRectangle.bottomRightRadius + checked: root.checked + show: root.activeFocus + topLeftRadius: backgroundRectangle.topLeftRadius + topRightRadius: backgroundRectangle.topRightRadius + } + } + contentItem: Row { + spacing: Style.dimens.text_spacing + + TintableIcon { + id: icon + + anchors.verticalCenter: parent.verticalCenter + source: root.icon.source + sourceSize.height: Style.dimens.small_icon_size + tintColor: colors.controlContent + } + GText { + id: label + + Accessible.ignored: true + anchors.verticalCenter: parent.verticalCenter + color: colors.controlContent + text: root.name + } + } + + Accessible.onPressAction: toggled() // Windows + Accessible.onToggleAction: toggled() // macOS + + Keys.onSpacePressed: event => { + toggled(); + if (Qt.platform.os === "windows" && checked) { + //: LABEL DESKTOP + Accessible.announce(qsTr("checked")); + } + event.accepted = true; + } + + StatefulColors { + id: colors + + controlStyle: Style.color.controlRadiobutton + groupMember: true + statefulControl: root + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/GRadioButtonFocusFrame.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/GRadioButtonFocusFrame.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/GRadioButtonFocusFrame.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/GRadioButtonFocusFrame.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick +import Governikus.Style +import Governikus.Type + +Rectangle { + id: root + + property bool checked: false + property bool show: false + + anchors.fill: parent + anchors.margins: -(border.width * 2) + border.color: Style.color.focus_indicator + border.width: Style.dimens.border_width * 2 + color: Style.color.transparent + visible: { + if (Qt.platform.os !== "windows" && ApplicationModel.screenReaderRunning) + return false; + if (show && UiPluginModel.showFocusIndicator) + return true; + return false; + } + + // override top and bottom border corner of previous and next button in group + Frame { + border.color: Style.color.pane.background.basic + border.width: root.border.width + + // draw underlying button border to have also the left border + Frame { + border.color: root.checked ? Style.color.controlRadiobutton.border.checked : Style.color.controlRadiobutton.border.basic + border.width: Style.dimens.border_width + } + } + + component Frame: Rectangle { + anchors.fill: parent + anchors.margins: (parent as Rectangle).border.width + bottomLeftRadius: (parent as Rectangle).bottomLeftRadius + bottomRightRadius: (parent as Rectangle).bottomRightRadius + color: "transparent" + topLeftRadius: (parent as Rectangle).topLeftRadius + topRightRadius: (parent as Rectangle).topRightRadius + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/GeneralSettings.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/GeneralSettings.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/GeneralSettings.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/GeneralSettings.qml 2025-10-30 10:10:48.000000000 +0000 @@ -12,11 +12,12 @@ ColumnLayout { id: root + signal showUpdateRequested + spacing: Style.dimens.pane_spacing GPane { Layout.fillWidth: true - spacing: Style.dimens.pane_spacing //: LABEL DESKTOP title: qsTr("Change language") @@ -24,13 +25,12 @@ Utils.positionViewAtItem(this) LanguageButtons { - columns: 4 } } GPane { Layout.fillWidth: true contentPadding: 0 - spacing: 0 + contentSpacing: Style.dimens.pane_spacing / 2 //: LABEL DESKTOP title: qsTr("Appearance") @@ -38,6 +38,8 @@ Utils.positionViewAtItem(this) DarkModeButtons { + Layout.leftMargin: Style.dimens.pane_padding + Layout.rightMargin: Style.dimens.pane_padding } GSwitch { Layout.fillWidth: true @@ -47,7 +49,7 @@ description: qsTr("Toggling will restart the %1").arg(Qt.application.name) drawBottomCorners: true //: LABEL DESKTOP - text: qsTr("Use the system font") + text: qsTr("Use system font") onCheckedChanged: { if (checked !== SettingsModel.useSystemFont) { @@ -62,7 +64,7 @@ GPane { Layout.fillWidth: true contentPadding: 0 - spacing: 0 + contentSpacing: 0 //: LABEL DESKTOP title: qsTr("Accessibility") @@ -97,7 +99,7 @@ GPane { Layout.fillWidth: true contentPadding: 0 - spacing: 0 + contentSpacing: 0 //: LABEL DESKTOP title: qsTr("Behavior") @@ -145,25 +147,66 @@ } GSwitch { Layout.fillWidth: true - checked: SettingsModel.showInAppNotifications - - //: LABEL DESKTOP Only visible when the user activates the developer mode in the settings. - description: SettingsModel.developerMode ? qsTr("Using the developer mode forces the notifications to be enabled.") : "" - drawBottomCorners: true - enabled: !SettingsModel.developerMode + checked: SettingsModel.autoUpdateCheck + //: LABEL DESKTOP %1 is replaced with the application name + description: qsTr("When you start %1, it automatically checks for updates. Updates are not performed automatically. If this option is disabled, you have to manually check for updates in the settings.").arg(Qt.application.name) + enabled: !SettingsModel.autoUpdateCheckSetByAdmin && !SettingsModel.appUpdateData.appcastRunning + + //: LABEL DESKTOP + text: qsTr("Automatically check for software updates at program start (recommended)") + visible: SettingsModel.autoUpdateAvailable + + onCheckedChanged: SettingsModel.autoUpdateCheck = checked + } + ColumnLayout { + readonly property bool isCheckingForUpdate: update.appcastRunning + readonly property var update: SettingsModel.appUpdateData + readonly property bool updateAvailable: SettingsModel.appUpdateData.updateAvailable + readonly property bool updateValid: SettingsModel.appUpdateData.valid + + Layout.bottomMargin: Style.dimens.pane_padding + Layout.leftMargin: Style.dimens.pane_padding + Layout.rightMargin: Style.dimens.pane_padding + spacing: Style.dimens.pane_spacing + visible: SettingsModel.autoUpdateAvailable + + GText { + color: (parent.updateAvailable || !parent.updateValid) ? Style.color.textNormal.basic : Style.color.textSubline.basic + text: parent.update.appcastStatus + visible: text !== "" + } + GButton { + //: LABEL DESKTOP + text: qsTr("Show update") + visible: parent.updateAvailable - //: LABEL DESKTOP - text: qsTr("Show notifications inside of %1").arg(Qt.application.name) + onClicked: root.showUpdateRequested() + } + GProgressBar { + Layout.fillWidth: true + text: "%1 %".arg(Math.floor(value)) + value: 100 * parent.update.appcastProgress / parent.update.appcastTotal + visible: parent.isCheckingForUpdate + } + GLink { + colorStyle: Style.color.linkTitle + font.underline: true + horizontalPadding: 0 + text: !parent.isCheckingForUpdate ? + //: LABEL DESKTOP + qsTr("Start manual search for software update") : + //: LABEL DESKTOP + qsTr("Abort search") + verticalPadding: 0 + visible: !parent.updateAvailable - onCheckedChanged: SettingsModel.showInAppNotifications = checked - onFocusChanged: if (focus) - Utils.positionViewAtItem(this) + onClicked: !parent.isCheckingForUpdate ? SettingsModel.updateAppcast() : SettingsModel.appUpdateData.abortDownload() + } } } GPane { Layout.fillWidth: true contentPadding: 0 - spacing: Style.dimens.pane_spacing //: LABEL DESKTOP title: qsTr("Network") visible: SettingsModel.customProxyAttributesPresent diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/SecurityAndPrivacySettings.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/SecurityAndPrivacySettings.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+desktop/internal/SecurityAndPrivacySettings.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+desktop/internal/SecurityAndPrivacySettings.qml 2025-10-30 10:10:48.000000000 +0000 @@ -15,7 +15,7 @@ GPane { Layout.fillWidth: true contentPadding: 0 - spacing: 0 + contentSpacing: 0 //: LABEL DESKTOP title: qsTr("Numeric keypad") @@ -43,60 +43,4 @@ onCheckedChanged: SettingsModel.visualPrivacy = checked } } - GPane { - Layout.fillWidth: true - contentPadding: 0 - spacing: Style.dimens.pane_spacing - //: LABEL DESKTOP - title: qsTr("Software updates") - visible: SettingsModel.autoUpdateAvailable - - GSwitch { - Layout.fillWidth: true - checked: SettingsModel.autoUpdateCheck - enabled: !SettingsModel.autoUpdateCheckSetByAdmin - - //: LABEL DESKTOP - text: qsTr("Check for updates at program start") - - onCheckedChanged: SettingsModel.autoUpdateCheck = checked - } - RowLayout { - readonly property bool updateAvailable: SettingsModel.appUpdateData.updateAvailable - readonly property bool updateValid: SettingsModel.appUpdateData.valid - - Layout.bottomMargin: Style.dimens.pane_padding - Layout.leftMargin: Style.dimens.pane_padding - Layout.rightMargin: Style.dimens.pane_padding - spacing: Style.dimens.pane_spacing - - GButton { - text: (parent.updateAvailable ? - //: LABEL DESKTOP - qsTr("Show update") : - //: LABEL DESKTOP - qsTr("Check now")) - - onClicked: SettingsModel.updateAppcast() - } - GText { - color: (parent.updateAvailable || !parent.updateValid) ? Style.color.textNormal.basic : Style.color.textSubline.basic - text: { - if (parent.updateAvailable && parent.updateValid) { - //: LABEL DESKTOP An update is available, the new version is supplied to the user. - return qsTr("An update is available (version %1)!").arg(SettingsModel.appUpdateData.version); - } else if (parent.updateAvailable && !parent.updateValid) { - //: LABEL DESKTOP The updater found an update but not all required update information are valid, this should be a very rare case. - return qsTr("An update is available but retrieving the information failed."); - } else if (!parent.updateAvailable && parent.updateValid) { - //: LABEL DESKTOP The current version is up to date, no user action is required. - return qsTr("Your version %1 of %2 is up to date.").arg(Qt.application.version).arg(Qt.application.name); - } else { - //: LABEL DESKTOP The automatic update check is disabled (or no network connection was present during app start), a manual check for update is required. - return qsTr("No update information available, please check for update manually."); - } - } - } - } - } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/DarkModeButtons.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/DarkModeButtons.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/DarkModeButtons.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/DarkModeButtons.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,77 +1,33 @@ /** * Copyright (c) 2023-2025 Governikus GmbH & Co. KG, Germany */ + +pragma ComponentBehavior: Bound + import QtQuick -import QtQuick.Layouts + import Governikus.Global import Governikus.Type -ColumnLayout { +GRepeater { id: root - readonly property var checkedButton: system.mode === SettingsModel.userDarkMode ? system : dark.mode === SettingsModel.userDarkMode ? dark : light - readonly property string selectedIconPath: checkedButton.image - readonly property string selectedText: checkedButton.text - signal buttonClicked - function onAppearanceButtonClicked(mode) { - if (SettingsModel.userDarkMode === mode) - return; - SettingsModel.userDarkMode = mode; - root.buttonClicked(); - } - - spacing: 0 - - Component.onCompleted: { - if (!UiPluginModel.osDarkModeSupported) - system.visible = false; - } - - GCollapsibleSubButton { - id: system - - readonly property var mode: SettingsModel.ModeOption.AUTO + delegate: GRadioButton { + required property int value - //: LABEL ALL_PLATFORMS - Accessible.description: qsTr("Set the app appearance to system mode") - Layout.fillWidth: true - image: "qrc:///images/appearance_system_mode.svg" - //: LABEL ALL_PLATFORMS - text: qsTr("System") + checked: SettingsModel.userDarkMode === value tintIcon: true - onClicked: root.onAppearanceButtonClicked(mode) + onClicked: { + if (SettingsModel.userDarkMode === value) + return; + + SettingsModel.userDarkMode = value; + root.buttonClicked(); + } } - GCollapsibleSubButton { - id: dark - - readonly property var mode: SettingsModel.ModeOption.ON - - //: LABEL ALL_PLATFORMS - Accessible.description: qsTr("Set the app appearance to dark mode") - Layout.fillWidth: true - image: "qrc:///images/appearance_dark_mode.svg" - //: LABEL ALL_PLATFORMS - text: qsTr("Dark") - tintIcon: true - - onClicked: root.onAppearanceButtonClicked(mode) - } - GCollapsibleSubButton { - id: light - - readonly property var mode: SettingsModel.ModeOption.OFF - - //: LABEL ALL_PLATFORMS - Accessible.description: qsTr("Set the app appearance to light mode") - Layout.fillWidth: true - image: "qrc:///images/appearance_light_mode.svg" - //: LABEL ALL_PLATFORMS - text: qsTr("Light") - tintIcon: true - - onClicked: root.onAppearanceButtonClicked(mode) + model: DarkModeButtonData { } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/LanguageButtons.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/LanguageButtons.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/LanguageButtons.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/LanguageButtons.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany + */ + +pragma ComponentBehavior: Bound + +import QtQuick + +import Governikus.Global +import Governikus.Type + +GRepeater { + id: root + + signal buttonClicked + + delegate: GRadioButton { + required property string value + + checked: SettingsModel.language === value + + onClicked: { + if (SettingsModel.language === value) + return; + + SettingsModel.language = value; + root.buttonClicked(); + } + } + model: LanguageButtonData { + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/SettingsView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/SettingsView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/SettingsView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/SettingsView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -18,21 +18,16 @@ FlickableSectionPage { id: root - function platformId(pName) { - return "mobile," + pName.replace(" ", "").toLowerCase(); - } - enableTileStyle: false spacing: Style.dimens.pane_spacing //: LABEL ANDROID IOS title: qsTr("Settings") GOptionsContainer { - Layout.fillWidth: true //: LABEL ANDROID IOS title: qsTr("General") - GCollapsible { + GRadioGroup { id: languageCollapsible contentBottomMargin: 0 @@ -40,84 +35,44 @@ contentSpacing: 0 contentTopMargin: 0 drawTopCorners: true - selectionIcon: "qrc:///images/location_flag_%1.svg".arg(SettingsModel.language) - selectionTitle: { - switch (SettingsModel.language) { - case "de": - return "Deutsch"; - case "ru": - return "РуÑÑкий"; - case "uk": - return "УкраїнÑька"; - default: - return "English"; - } - } //: LABEL ANDROID IOS title: qsTr("Change language") - width: parent.width - GRepeater { - delegate: GCollapsibleSubButton { - required property string a11yDescription - required property string a11yName - required property string language - required property string languageText - - Accessible.description: a11yDescription - Accessible.name: a11yName - Layout.fillWidth: true - text: languageText - - onClicked: { - SettingsModel.language = language; - languageCollapsible.expanded = false; - } - } - model: LanguageButtonData { - } + LanguageButtons { + id: languageButtons + + onButtonClicked: languageCollapsible.onOptionSelected() } } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } - GCollapsible { + GRadioGroup { id: appearanceCollapsible contentBottomMargin: 0 contentHorizontalMargin: 0 contentSpacing: 0 contentTopMargin: 0 - selectionIcon: modeButtons.selectedIconPath - selectionTitle: modeButtons.selectedText tintIcon: true //: LABEL ANDROID IOS title: qsTr("Appearance") - width: parent.width DarkModeButtons { id: modeButtons width: parent.width - onButtonClicked: appearanceCollapsible.expanded = false + onButtonClicked: appearanceCollapsible.onOptionSelected() } } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } GSwitch { + Layout.fillWidth: true //: LABEL ANDROID IOS description: qsTr("Toggling will restart the %1").arg(Qt.application.name) //: LABEL ANDROID IOS text: qsTr("Use system font") - width: parent.width Component.onCompleted: { checked = SettingsModel.useSystemFont; @@ -129,19 +84,18 @@ } } } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { + visible: technologySwitch.visible } TechnologySwitch { + id: technologySwitch + contentBottomMargin: 0 contentHorizontalMargin: 0 contentSpacing: 0 contentTopMargin: 0 drawBottomCorners: true - width: parent.width + visible: hasSelection } } GOptionsContainer { @@ -150,43 +104,35 @@ title: qsTr("Accessibility") GSwitch { + Layout.fillWidth: true checked: !SettingsModel.useAnimations drawTopCorners: true //: LABEL ANDROID IOS text: qsTr("Use images instead of animations") - width: parent.width onCheckedChanged: SettingsModel.useAnimations = !checked } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } GSwitch { + Layout.fillWidth: true checked: SettingsModel.visualPrivacy //: LABEL ANDROID IOS text: qsTr("Hide key animations when entering PIN") - width: parent.width onCheckedChanged: SettingsModel.visualPrivacy = checked } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } GSwitch { + Layout.fillWidth: true checked: !SettingsModel.autoRedirectAfterAuthentication //: LABEL ANDROID IOS description: qsTr("After identification, you will only be redirected back to the provider after confirmation. Otherwise, you will be redirected automatically after a few seconds.") drawBottomCorners: true //: LABEL ANDROID IOS text: qsTr("Manual redirection back to the provider") - width: parent.width onCheckedChanged: SettingsModel.autoRedirectAfterAuthentication = !checked } @@ -204,7 +150,6 @@ selectionTitle: expanded ? "" : SettingsModel.deviceName //: LABEL ANDROID IOS title: qsTr("Device name") - width: parent.width GTextField { function saveInput() { @@ -218,54 +163,43 @@ text: SettingsModel.deviceName onAccepted: saveInput() - onFocusChanged: focus ? root.positionViewAtItem(this) : saveInput() + onFocusChanged: if (!focus) + saveInput() } } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } GSwitch { + Layout.fillWidth: true checked: SettingsModel.pinPadMode //: LABEL ANDROID IOS text: qsTr("Enter PIN on this device") - width: parent.width onCheckedChanged: SettingsModel.pinPadMode = checked } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } GSwitch { + Layout.fillWidth: true checked: SettingsModel.showAccessRights enabled: SettingsModel.pinPadMode //: LABEL ANDROID IOS text: qsTr("Show requested rights on this device as well") - width: parent.width onCheckedChanged: SettingsModel.showAccessRights = checked } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } GMenuItem { + Layout.fillWidth: true //: LABEL ANDROID IOS description: qsTr("Add and remove devices") drawBottomCorners: true //: LABEL ANDROID IOS title: qsTr("Manage pairings") - width: parent.width onClicked: root.push(remoteServiceSettings) @@ -287,6 +221,7 @@ title: qsTr("Numeric keypad") GSwitch { + Layout.fillWidth: true checked: SettingsModel.shuffleScreenKeyboard //: LABEL ANDROID IOS description: qsTr("Makes it difficult for outsiders to detect PIN entry") @@ -294,17 +229,13 @@ //: LABEL ANDROID IOS text: qsTr("Shuffle keys") - width: parent.width onCheckedChanged: SettingsModel.shuffleScreenKeyboard = checked } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } GSwitch { + Layout.fillWidth: true checked: SettingsModel.visualPrivacy //: LABEL ANDROID IOS description: qsTr("Makes it difficult for outsiders to detect PIN entry") @@ -312,7 +243,6 @@ //: LABEL ANDROID IOS text: qsTr("Hide key animations") - width: parent.width onCheckedChanged: SettingsModel.visualPrivacy = checked } @@ -321,14 +251,14 @@ Layout.fillWidth: true //: LABEL ANDROID IOS title: qsTr("Smart-eID") - visible: ApplicationModel.isSmartSupported + visible: ApplicationModel.smartSupported GMenuItem { + Layout.fillWidth: true //: LABEL ANDROID IOS description: qsTr("Reset Smart-eID data on your device") //: LABEL ANDROID IOS title: qsTr("Reset Smart-eID") - width: parent.width onClicked: root.push(smartDeleteView) @@ -347,29 +277,25 @@ visible: SettingsModel.advancedSettings GSwitch { + Layout.fillWidth: true checked: SettingsModel.enableCanAllowed drawTopCorners: true //: LABEL ANDROID IOS text: qsTr("Support CAN allowed mode for on-site reading") - width: parent.width onCheckedChanged: SettingsModel.enableCanAllowed = checked } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } GSwitch { + Layout.fillWidth: true checked: SettingsModel.skipRightsOnCanAllowed drawBottomCorners: true enabled: SettingsModel.enableCanAllowed //: LABEL ANDROID IOS text: qsTr("Skip rights page") - width: parent.width onCheckedChanged: SettingsModel.skipRightsOnCanAllowed = checked } @@ -383,6 +309,7 @@ GSwitch { id: testUriSwitch + Layout.fillWidth: true checked: SettingsModel.useSelfauthenticationTestUri //: LABEL ANDROID IOS description: qsTr("Allow test sample card usage") @@ -390,17 +317,13 @@ //: LABEL ANDROID IOS text: qsTr("Testmode for the self-authentication") - width: parent.width onCheckedChanged: SettingsModel.useSelfauthenticationTestUri = checked } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } GSwitch { + Layout.fillWidth: true checked: SettingsModel.enableSimulator //: LABEL ANDROID IOS description: qsTr("Simulate a test sample card in authentications") @@ -408,7 +331,6 @@ //: LABEL ANDROID IOS text: qsTr("Internal card simulator") - width: parent.width onCheckedChanged: SettingsModel.enableSimulator = checked } @@ -421,6 +343,7 @@ visible: UiPluginModel.debugBuild GSwitch { + Layout.fillWidth: true checked: SettingsModel.developerMode //: LABEL ANDROID IOS description: qsTr("Use a more tolerant mode") @@ -428,24 +351,19 @@ //: LABEL ANDROID IOS text: qsTr("Developer mode") - width: parent.width onCheckedChanged: SettingsModel.developerMode = checked } - GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + SettingsViewSeparator { } GMenuItem { + Layout.fillWidth: true //: LABEL ANDROID IOS description: qsTr("Show Transport PIN reminder, store feedback and close reminder dialogs.") drawBottomCorners: true icon.source: "qrc:///images/material_refresh.svg" //: LABEL ANDROID IOS title: qsTr("Reset hideable dialogs") - width: parent.width onClicked: SettingsModel.resetHideableDialogs() } @@ -458,9 +376,9 @@ visible: UiPluginModel.debugBuild Flow { + Layout.fillWidth: true padding: Style.dimens.pane_padding spacing: Style.dimens.pane_spacing - width: parent.width GButton { Layout.minimumWidth: implicitHeight @@ -498,7 +416,6 @@ ColumnLayout { spacing: Style.dimens.pane_spacing - width: parent.width GButton { Layout.leftMargin: Style.dimens.pane_padding @@ -509,7 +426,7 @@ text: qsTr("New Logfile") onClicked: { - LogModel.saveDummyLogFile(); + LogFilesModel.saveDummyLogFile(); ApplicationModel.showFeedback("Created new logfile."); } } @@ -524,10 +441,16 @@ onClicked: { let date = new Date(); date.setDate(new Date().getDate() - 15); - LogModel.saveDummyLogFile(date); + LogFilesModel.saveDummyLogFile(date); ApplicationModel.showFeedback("Created old logfile."); } } } } + + component SettingsViewSeparator: GSeparator { + Layout.fillWidth: true + Layout.leftMargin: Style.dimens.pane_spacing + Layout.rightMargin: Layout.leftMargin + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/internal/GRadioButton.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/internal/GRadioButton.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/internal/GRadioButton.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/internal/GRadioButton.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick +import QtQuick.Layouts + +import Governikus.Global +import Governikus.Style +import Governikus.View + +GAbstractButton { + id: root + + required property string desc + property bool drawBottomCorners: false + required property url img + required property string name + property alias tintIcon: icon.tintEnabled + + Accessible.description: desc + Accessible.name: name + Accessible.role: Accessible.RadioButton + Layout.fillWidth: true + autoExclusive: true + checkable: true + horizontalPadding: Style.dimens.pane_spacing * 2 + icon.source: img + text: name + verticalPadding: Style.dimens.pane_spacing / 2 + + background: RoundedRectangle { + bottomLeftCorner: root.drawBottomCorners + bottomRightCorner: root.drawBottomCorners + color: colors.paneBackground + topLeftCorner: false + topRightCorner: false + } + contentItem: RowLayout { + spacing: Style.dimens.pane_spacing + + TintableIcon { + id: icon + + source: root.icon.source + sourceSize.height: Style.dimens.small_icon_size + tintColor: label.color + tintEnabled: false + } + GText { + id: label + + Accessible.ignored: true + Layout.maximumWidth: Number.POSITIVE_INFINITY + color: colors.textNormal + text: root.text + } + } + + Accessible.onScrollDownAction: Utils.scrollPageDownOnGFlickable(this) + Accessible.onScrollUpAction: Utils.scrollPageUpOnGFlickable(this) + + StatefulColors { + id: colors + + checkedCondition: false + paneStyle: Style.color.paneSublevel + statefulControl: root + } + FocusFrame { + marginFactor: -1 + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/internal/GRadioGroup.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/internal/GRadioGroup.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/internal/GRadioGroup.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/internal/GRadioGroup.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick.Controls + +import Governikus.Global + +GCollapsible { + id: root + + selectionIcon: radioGroup.checkedButton ? radioGroup.checkedButton.icon.source : "" + selectionTitle: radioGroup.checkedButton ? radioGroup.checkedButton.text : "" + + ButtonGroup { + id: radioGroup + + buttons: root.content.filter(child => child instanceof GRadioButton) + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/internal/TechnologySwitch.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/internal/TechnologySwitch.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/+mobile/internal/TechnologySwitch.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/+mobile/internal/TechnologySwitch.qml 2025-10-30 10:10:48.000000000 +0000 @@ -5,78 +5,62 @@ pragma ComponentBehavior: Bound import QtQuick -import QtQuick.Layouts -import Governikus.Global import Governikus.Type -GCollapsible { +GRadioGroup { id: root - function getImage(pTechnology) { - switch (pTechnology) { - case ReaderManagerPluginType.NFC: - return "qrc:///images/mobile/icon_nfc.svg"; - case ReaderManagerPluginType.SMART: - return "qrc:///images/mobile/icon_smart.svg"; - case ReaderManagerPluginType.REMOTE_IFD: - return "qrc:///images/mobile/icon_remote.svg"; - case ReaderManagerPluginType.SIMULATOR: - return "qrc:///images/mobile/icon_simulator.svg"; - } - } - function getLabel(pTechnology) { - switch (pTechnology) { - case ReaderManagerPluginType.NFC: - //: LABEL ANDROID IOS - return qsTr("by NFC"); - case ReaderManagerPluginType.SMART: - //: LABEL ANDROID IOS - return qsTr("by Smart-eID"); - case ReaderManagerPluginType.REMOTE_IFD: - //: LABEL ANDROID IOS - return qsTr("by smartphone as card reader"); - case ReaderManagerPluginType.SIMULATOR: - //: LABEL ANDROID IOS - return qsTr("by internal card simulator"); - } - } + readonly property bool hasSelection: nfcSupported || simulatorEnabled || smartSupported + readonly property bool nfcSupported: ApplicationModel.nfcState !== ApplicationModel.NfcState.UNAVAILABLE + readonly property bool simulatorEnabled: SettingsModel.enableSimulator + readonly property bool smartSupported: ApplicationModel.smartSupported - selectionIcon: getImage(SettingsModel.preferredTechnology) - selectionTitle: getLabel(SettingsModel.preferredTechnology) tintIcon: true //: LABEL ANDROID IOS title: qsTr("Readout mode") width: parent.width TechnologyButton { - technology: ReaderManagerPluginType.NFC + img: "qrc:///images/mobile/icon_nfc.svg" + //: LABEL ANDROID IOS + name: qsTr("by NFC") + value: ReaderManagerPluginType.NFC + visible: root.nfcSupported } TechnologyButton { - technology: ReaderManagerPluginType.SMART - visible: ApplicationModel.isSmartSupported + img: "qrc:///images/mobile/icon_smart.svg" + //: LABEL ANDROID IOS + name: qsTr("by Smart-eID") + value: ReaderManagerPluginType.SMART + visible: root.smartSupported } TechnologyButton { drawBottomCorners: !SettingsModel.enableSimulator - technology: ReaderManagerPluginType.REMOTE_IFD + img: "qrc:///images/mobile/icon_remote.svg" + //: LABEL ANDROID IOS + name: qsTr("by smartphone as card reader") + value: ReaderManagerPluginType.REMOTE_IFD } TechnologyButton { drawBottomCorners: true - technology: ReaderManagerPluginType.SIMULATOR - visible: SettingsModel.enableSimulator + img: "qrc:///images/mobile/icon_simulator.svg" + //: LABEL ANDROID IOS + name: qsTr("by internal card simulator") + value: ReaderManagerPluginType.SIMULATOR + visible: root.simulatorEnabled } - component TechnologyButton: GCollapsibleSubButton { - required property int technology + component TechnologyButton: GRadioButton { + required property int value - Layout.fillWidth: true - image: root.getImage(technology) - text: root.getLabel(technology) + checked: SettingsModel.preferredTechnology === value + desc: "" tintIcon: true onClicked: { - SettingsModel.preferredTechnology = technology; - root.expanded = false; + SettingsModel.preferredTechnology = value; + root.onOptionSelected(); } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/internal/DarkModeButtonData.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/internal/DarkModeButtonData.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/internal/DarkModeButtonData.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/internal/DarkModeButtonData.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2023-2025 Governikus GmbH & Co. KG, Germany + */ + +import QtQuick +import Governikus.Type + +ListModel { + ListElement { + //: LABEL ALL_PLATFORMS + desc: qsTr("Set the app appearance to system mode") + img: "qrc:///images/appearance_system_mode.svg" + //: LABEL ALL_PLATFORMS + name: qsTr("System") + value: SettingsModel.ModeOption.AUTO + } + ListElement { + //: LABEL ALL_PLATFORMS + desc: qsTr("Set the app appearance to dark mode") + img: "qrc:///images/appearance_dark_mode.svg" + //: LABEL ALL_PLATFORMS + name: qsTr("Dark") + value: SettingsModel.ModeOption.ON + } + ListElement { + //: LABEL ALL_PLATFORMS + desc: qsTr("Set the app appearance to light mode") + img: "qrc:///images/appearance_light_mode.svg" + //: LABEL ALL_PLATFORMS + name: qsTr("Light") + value: SettingsModel.ModeOption.OFF + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/internal/LanguageButtonData.qml ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/internal/LanguageButtonData.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SettingsView/internal/LanguageButtonData.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SettingsView/internal/LanguageButtonData.qml 2025-10-30 10:10:48.000000000 +0000 @@ -6,38 +6,34 @@ ListModel { ListElement { //: LABEL ALL_PLATFORMS - a11yDescription: qsTr("Set language to german") + desc: qsTr("Set language to german") + img: "qrc:///images/location_flag_de.svg" //: LABEL ALL_PLATFORMS - a11yName: qsTr("German") - image: "qrc:///images/location_flag_de.svg" - language: "de" - languageText: "Deutsch" + name: qsTr("German") + value: "de" } ListElement { //: LABEL ALL_PLATFORMS - a11yDescription: qsTr("Set language to english") + desc: qsTr("Set language to english") + img: "qrc:///images/location_flag_en.svg" //: LABEL ALL_PLATFORMS - a11yName: qsTr("English") - image: "qrc:///images/location_flag_en.svg" - language: "en" - languageText: "English" + name: qsTr("English") + value: "en" } ListElement { //: LABEL ALL_PLATFORMS - a11yDescription: qsTr("Set language to ukrainian") + desc: qsTr("Set language to ukrainian") + img: "qrc:///images/location_flag_uk.svg" //: LABEL ALL_PLATFORMS - a11yName: qsTr("Ukrainian") - image: "qrc:///images/location_flag_uk.svg" - language: "uk" - languageText: "УкраїнÑька" + name: qsTr("Ukrainian") + value: "uk" } ListElement { //: LABEL ALL_PLATFORMS - a11yDescription: qsTr("Set language to russian") + desc: qsTr("Set language to russian") + img: "qrc:///images/location_flag_ru.svg" //: LABEL ALL_PLATFORMS - a11yName: qsTr("Russian") - image: "qrc:///images/location_flag_ru.svg" - language: "ru" - languageText: "РуÑÑкий" + name: qsTr("Russian") + value: "ru" } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/CheckSmartResultView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/CheckSmartResultView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/CheckSmartResultView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/CheckSmartResultView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -26,7 +26,7 @@ case SmartModel.State.UPDATING_STATUS: return smartUpdatingData; case SmartModel.State.UNAVAILABLE: - return smartUnvailableData; + return smartUnavailableData; case SmartModel.State.UNUSABLE: return smartUnusableData; case SmartModel.State.NO_PROVISIONING: @@ -68,7 +68,7 @@ title: qsTr("Updating Smart-eID status...") } SuggestionData { - id: smartUnvailableData + id: smartUnavailableData animationSymbol: Symbol.Type.ERROR continueButtonIcon: "qrc:///images/device_button.svg" diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/PersonalizationController.qml ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/PersonalizationController.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/PersonalizationController.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/PersonalizationController.qml 2025-10-30 10:10:48.000000000 +0000 @@ -152,7 +152,6 @@ break; case "StateGetSessionId": skipSelectReader = true; - showRemoveCardFeedback(PersonalizationModel, false); PersonalizationModel.continueWorkflow(); break; case "StateEnterNewPacePin": diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/PersonalizationResultView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/PersonalizationResultView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/PersonalizationResultView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/PersonalizationResultView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -25,7 +25,7 @@ qsTr("Back to start page") : //: LABEL ANDROID IOS qsTr("Continue") - errorCode: PersonalizationModel.error ? PersonalizationModel.statusCodeString : "" + errorCode: PersonalizationModel.error ? PersonalizationModel.statusCodeDisplayString : "" errorDescription: PersonalizationModel.error ? PersonalizationModel.errorText : "" //: LABEL ANDROID IOS header: qsTr("Personalization failed") @@ -58,7 +58,7 @@ visible: PersonalizationModel.blockingCode !== "" } GText { - font.weight: Font.Bold + font.weight: Style.font.bold //: INFO ANDROID IOS Placeholder (error) text if the Smart-eID setup finished successfully but for some reason no blocking code was retrieved text: qsTr("The Smart-eID setup finished successfully but no blocking code was retrieved. For security reasons, you should delete your Smart-eID and restart the setup.") visible: PersonalizationModel.blockingCode === "" diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/SmartDeleteBaseView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/SmartDeleteBaseView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/SmartDeleteBaseView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/SmartDeleteBaseView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -73,7 +73,12 @@ onCancelClicked: root.close() onContinueClicked: root.close() - onMailClicked: LogModel.mailLog("support@ausweisapp.de", PersonalizationModel.getEmailHeader(), PersonalizationModel.getEmailBody()) + onMailClicked: logModel.mailLogFile("support@ausweisapp.de", PersonalizationModel.getEmailHeader(), PersonalizationModel.getEmailBody()) + + LogModel { + id: logModel + + } } } TintableIcon { @@ -89,13 +94,11 @@ GText { id: deleteDescription - width: parent.width } GText { //: LABEL ANDROID IOS text: qsTr("If you want to use that functionality again, you need to set up a new Smart-eID first.") - width: parent.width } } GSpacer { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/SmartMainView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/SmartMainView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/SmartMainView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/SmartMainView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -28,9 +28,7 @@ Layout.bottomMargin: Style.dimens.pane_spacing Layout.maximumHeight: Style.dimens.header_icon_size } - GText { - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter + Heading { text: { switch (root.smartState) { case SmartModel.State.UPDATING_STATUS: @@ -43,7 +41,6 @@ return ""; } } - textStyle: Style.text.headline visible: !root.showCheck wrapMode: Text.WordWrap } @@ -84,13 +81,11 @@ //: LABEL ANDROID IOS text: qsTr("With the Smart-eID you may also use the online identification function without the ID card.") - width: parent.width } GText { //: LABEL ANDROID IOS text: qsTr("Check here if your device is suitable to set up a Smart-eID.") - width: parent.width } } GSpacer { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/SmartSettingsView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/SmartSettingsView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/SmartSettingsView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/SmartSettingsView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -27,64 +27,61 @@ title: qsTr("Smart-eID") GMenuItem { + Layout.fillWidth: true //: LABEL ANDROID IOS description: qsTr("Show Smart-eID data") //: LABEL ANDROID IOS title: qsTr("Try Smart-eID") visible: root.smartState === SmartModel.State.READY - width: parent.width onClicked: root.startSelfAuth() } GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + Layout.fillWidth: true + Layout.leftMargin: Style.dimens.pane_spacing + Layout.rightMargin: Layout.leftMargin } GMenuItem { + Layout.fillWidth: true //: LABEL ANDROID IOS description: qsTr("Change the chosen Smart-eID PIN") //: LABEL ANDROID IOS title: qsTr("Change Smart-eID PIN") visible: root.smartState === SmartModel.State.READY - width: parent.width onClicked: root.changePin() } GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + Layout.fillWidth: true + Layout.leftMargin: Style.dimens.pane_spacing + Layout.rightMargin: Layout.leftMargin } GMenuItem { + Layout.fillWidth: true //: LABEL ANDROID IOS description: qsTr("Renew your Smart-eID with current data") //: LABEL ANDROID IOS title: qsTr("Renew Smart-eID") visible: root.smartState === SmartModel.State.READY - width: parent.width onClicked: root.updateSmart() } GSeparator { - anchors.left: parent.left - anchors.leftMargin: Style.dimens.pane_spacing - anchors.right: parent.right - anchors.rightMargin: Style.dimens.pane_spacing + Layout.fillWidth: true + Layout.leftMargin: Style.dimens.pane_spacing + Layout.rightMargin: Layout.leftMargin } GMenuItem { + Layout.fillWidth: true //: LABEL ANDROID IOS description: qsTr("Delete Smart-eID data from your device") //: LABEL ANDROID IOS title: qsTr("Delete Smart-eID") visible: root.smartState === SmartModel.State.READY - width: parent.width onClicked: root.deletePersonalization() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/SmartSetupStartView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/SmartSetupStartView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/SmartSetupStartView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/SmartSetupStartView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -41,11 +41,9 @@ //: LABEL ANDROID IOS text: qsTr("You are about to set up a Smart-eID on your device. In order to proceed, you need you ID card, your 6-digit ID card PIN and an internet connection.") - width: parent.width } PrivacyStatement { smart: true - width: parent.width } } GSpacer { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/SmartUpdateStartView.qml ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/SmartUpdateStartView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/SmartView/+mobile/internal/SmartUpdateStartView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/SmartView/+mobile/internal/SmartUpdateStartView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -42,18 +42,15 @@ //: LABEL ANDROID IOS text: qsTr("You are about to renew your Smart-eID. In order to proceed, you need your ID card, your 6-digit ID card PIN and an internet connection.") - width: parent.width } GText { - font.weight: Font.Bold + font.weight: Style.font.bold //: LABEL ANDROID IOS text: qsTr("Please note that your current Smart-eID is invalidated during the process and will not be usable until the update process is completed.") - width: parent.width } PrivacyStatement { smart: true - width: parent.width } } GSpacer { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Style/+desktop/internal/PlatformColorsContrast.qml ausweisapp2-2.4.0/src/ui/qml/modules/Style/+desktop/internal/PlatformColorsContrast.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Style/+desktop/internal/PlatformColorsContrast.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Style/+desktop/internal/PlatformColorsContrast.qml 2025-10-30 10:10:48.000000000 +0000 @@ -21,7 +21,6 @@ error: "#ff0000" focus_indicator: textNormal.basic image: "#ff0000" - progressbar_text: textNormal.basic success: "#00ff00" warning: "#ff0000" @@ -38,8 +37,17 @@ content.checked: root.textNormal.basic } controlRadiobutton: DefaultControlColors { - background.pressed: root.textNormal.basic - content.basic: root.textNormal.basic + background.checked: root.palette.highlight + background.hovered: background.checked + background.pressed: background.hovered + border.basic: root.palette.buttonText + border.checked: background.checked + border.hovered: border.checked + border.pressed: border.hovered + content.basic: border.basic + content.checked: root.palette.highlightedText + content.hovered: content.checked + content.pressed: content.hovered } controlScrollbar: DefaultControlColors { background.basic: root.textNormal.basic diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Style/+desktop/internal/PlatformDimensions.qml ausweisapp2-2.4.0/src/ui/qml/modules/Style/+desktop/internal/PlatformDimensions.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Style/+desktop/internal/PlatformDimensions.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Style/+desktop/internal/PlatformDimensions.qml 2025-10-30 10:10:48.000000000 +0000 @@ -5,52 +5,48 @@ import Governikus.Type QtObject { - readonly property real border_width: Math.max(Math.floor(UiPluginModel.scaleFactor * 2), 1) - readonly property int control_horizontalPadding: UiPluginModel.scaleFactor * 30 - readonly property real control_radius: UiPluginModel.scaleFactor * 26 - readonly property int control_verticalPadding: UiPluginModel.scaleFactor * 10 - readonly property int groupbox_spacing: UiPluginModel.scaleFactor * 20 - readonly property real header_icon_size: UiPluginModel.scaleFactor * 200 - readonly property real huge_icon_size: UiPluginModel.scaleFactor * 125 - readonly property real icon_size: UiPluginModel.scaleFactor * 48 + readonly property real border_width: Math.max(Math.floor(UiPluginModel.scaleFactor * 1.2), 1) + readonly property int control_horizontalPadding: UiPluginModel.scaleFactor * 18 + readonly property real control_radius: UiPluginModel.scaleFactor * 15.6 + readonly property int control_verticalPadding: UiPluginModel.scaleFactor * 6 + readonly property int groupbox_spacing: UiPluginModel.scaleFactor * 12 + readonly property real header_icon_size: UiPluginModel.scaleFactor * 120 + readonly property real huge_icon_size: UiPluginModel.scaleFactor * 75 + readonly property real icon_size: UiPluginModel.scaleFactor * 28.8 readonly property bool isLongLanguage: SettingsModel.language === "uk" || SettingsModel.language === "ru" - readonly property real large_icon_size: UiPluginModel.scaleFactor * 72 - readonly property real lineHeight: UiPluginModel.scaleFactor * 40 - readonly property real lineHeight_button: UiPluginModel.scaleFactor * 27 - readonly property real lineHeight_headline: UiPluginModel.scaleFactor * 47 - readonly property real lineHeight_navigation: UiPluginModel.scaleFactor * 63 - readonly property real lineHeight_subline: UiPluginModel.scaleFactor * 42 - readonly property real lineHeight_tile: UiPluginModel.scaleFactor * 63 - readonly property real lineHeight_title: UiPluginModel.scaleFactor * 70 - readonly property real max_text_width: UiPluginModel.scaleFactor * (isLongLanguage ? 1250 : 1000) - readonly property real medium_icon_size: UiPluginModel.scaleFactor * 64 - readonly property real min_button_width: UiPluginModel.scaleFactor * 220 - readonly property int pane_border_highlight_width: UiPluginModel.scaleFactor * 4 - readonly property int pane_padding: UiPluginModel.scaleFactor * 30 - readonly property real pane_radius: UiPluginModel.scaleFactor * 34 - readonly property int pane_spacing: UiPluginModel.scaleFactor * 30 - readonly property real popup_border: Math.max(UiPluginModel.scaleFactor * 2, 1) - readonly property real progress_bar_border: separator_size_large - readonly property real progress_bar_height: UiPluginModel.scaleFactor * 60 - readonly property real scrollbar_padding_horizontal: UiPluginModel.scaleFactor * 5 + readonly property real large_icon_size: UiPluginModel.scaleFactor * 43.2 + readonly property real lineHeight: UiPluginModel.scaleFactor * 24 + readonly property real lineHeight_button: UiPluginModel.scaleFactor * 16.2 + readonly property real lineHeight_headline: UiPluginModel.scaleFactor * 28.2 + readonly property real lineHeight_navigation: UiPluginModel.scaleFactor * 37.8 + readonly property real lineHeight_subline: UiPluginModel.scaleFactor * 25.2 + readonly property real lineHeight_tile: UiPluginModel.scaleFactor * 37.8 + readonly property real lineHeight_title: UiPluginModel.scaleFactor * 42 + readonly property real max_text_width: UiPluginModel.scaleFactor * (isLongLanguage ? 750 : 600) + readonly property real medium_icon_size: UiPluginModel.scaleFactor * 38.4 + readonly property real min_button_width: UiPluginModel.scaleFactor * 132 + readonly property int pane_border_highlight_width: UiPluginModel.scaleFactor * 2.4 + readonly property int pane_padding: UiPluginModel.scaleFactor * 18 + readonly property real pane_radius: UiPluginModel.scaleFactor * 20.4 + readonly property int pane_spacing: UiPluginModel.scaleFactor * 18 + readonly property real popup_border: Math.max(UiPluginModel.scaleFactor * 1.2, 1) + readonly property real scrollbar_padding_horizontal: UiPluginModel.scaleFactor * 3 readonly property real scrollbar_padding_vertical: UiPluginModel.scaleFactor * 0 - readonly property real scrollbar_width: UiPluginModel.scaleFactor * 10 - readonly property real separator_size: Math.max(Math.floor(UiPluginModel.scaleFactor * 2), 1) - readonly property real separator_size_large: Math.max(UiPluginModel.scaleFactor * 4, 1) - readonly property real small_icon_size: UiPluginModel.scaleFactor * 32 - readonly property real stagedprogressbar_height: UiPluginModel.scaleFactor * 13 - readonly property real stagedprogressbar_spacing: UiPluginModel.scaleFactor * 8 - readonly property real status_icon_medium: UiPluginModel.scaleFactor * 200 - readonly property real status_icon_small: UiPluginModel.scaleFactor * 100 - readonly property int subtext_spacing: Math.max(1, UiPluginModel.scaleFactor * 3) - readonly property real switch_width: UiPluginModel.scaleFactor * 97 - readonly property real text: UiPluginModel.scaleFactor * 27 - readonly property real textHeadline: UiPluginModel.scaleFactor * 40 - readonly property real textSubline: UiPluginModel.scaleFactor * 30 - readonly property real textTitle: UiPluginModel.scaleFactor * 60 - readonly property real text_navigation: UiPluginModel.scaleFactor * 50 - readonly property int text_spacing: UiPluginModel.scaleFactor * 10 - readonly property real text_tile: UiPluginModel.scaleFactor * 50 - readonly property real titlebar_padding: UiPluginModel.scaleFactor * 20 - readonly property real titlepane_radius: UiPluginModel.scaleFactor * 50 + readonly property real scrollbar_width: UiPluginModel.scaleFactor * 6 + readonly property real small_icon_size: UiPluginModel.scaleFactor * 19.2 + readonly property real stagedprogressbar_height: UiPluginModel.scaleFactor * 7.8 + readonly property real stagedprogressbar_spacing: UiPluginModel.scaleFactor * 4.8 + readonly property real status_icon_medium: UiPluginModel.scaleFactor * 120 + readonly property real status_icon_small: UiPluginModel.scaleFactor * 60 + readonly property int subtext_spacing: Math.max(1, UiPluginModel.scaleFactor * 1.8) + readonly property real switch_width: UiPluginModel.scaleFactor * 58.2 + readonly property real text: UiPluginModel.scaleFactor * 16.2 + readonly property real textHeadline: UiPluginModel.scaleFactor * 24 + readonly property real textSubline: UiPluginModel.scaleFactor * 18 + readonly property real textTitle: UiPluginModel.scaleFactor * 36 + readonly property real text_navigation: UiPluginModel.scaleFactor * 30 + readonly property int text_spacing: UiPluginModel.scaleFactor * 6 + readonly property real text_tile: UiPluginModel.scaleFactor * 30 + readonly property real titlebar_padding: UiPluginModel.scaleFactor * 12 + readonly property real titlepane_radius: UiPluginModel.scaleFactor * 30 } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Style/+mobile/internal/PlatformDimensions.qml ausweisapp2-2.4.0/src/ui/qml/modules/Style/+mobile/internal/PlatformDimensions.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Style/+mobile/internal/PlatformDimensions.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Style/+mobile/internal/PlatformDimensions.qml 2025-10-30 10:10:48.000000000 +0000 @@ -12,7 +12,7 @@ readonly property int control_radius: 12 readonly property int control_verticalPadding: 6 readonly property int groupbox_spacing: 10 - readonly property real header_icon_size: screenHeight / 7 + readonly property real header_icon_size: screenHeight / 8 readonly property int icon_size: 48 readonly property bool isLongLanguage: SettingsModel.language === "uk" || SettingsModel.language === "ru" readonly property int large_icon_size: 96 @@ -35,13 +35,10 @@ readonly property int pane_radius: 22 readonly property int pane_spacing: 20 readonly property int popup_border: 0 - readonly property int progress_bar_border: 2 - readonly property int progress_bar_height: 32 property int screenHeight: 0 readonly property int scrollbar_padding_horizontal: 0 readonly property int scrollbar_padding_vertical: 5 readonly property int scrollbar_width: 5 - readonly property int separator_size: 1 readonly property int small_icon_size: 24 readonly property int stagedprogressbar_height: 8 readonly property int stagedprogressbar_spacing: 5 diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Style/FontStyle.qml ausweisapp2-2.4.0/src/ui/qml/modules/Style/FontStyle.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Style/FontStyle.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Style/FontStyle.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ +import QtQuick + +import Governikus.Type + +QtObject { + id: root + + readonly property int bold: root.adjustFontWeight(Font.Bold) + readonly property int medium: root.adjustFontWeight(Font.Medium) + readonly property int normal: root.adjustFontWeight(Font.Normal) + + function adjustFontWeight(fontWeight) { + fontWeight += UiPluginModel.fontWeightAdjustment; + fontWeight = Math.max(1, fontWeight); + fontWeight = Math.min(fontWeight, 1000); + return fontWeight; + } +} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Style/Style.qml ausweisapp2-2.4.0/src/ui/qml/modules/Style/Style.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Style/Style.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Style/Style.qml 2025-10-30 10:10:48.000000000 +0000 @@ -13,6 +13,7 @@ readonly property var currentTheme: UiPluginModel.highContrastEnabled ? highContrastTheme : UiPluginModel.darkModeEnabled ? darkTheme : defaultTheme readonly property var dimens: currentTheme.dimens readonly property var flickDeceleration: Style.is_layout_desktop ? 7500.0 : 1500.0 + readonly property FontStyle font: fontStyles readonly property double scrolling_speed: 7500.0 property bool software_renderer: false readonly property var text: currentTheme.text @@ -56,4 +57,8 @@ id: textStyles } + FontStyle { + id: fontStyles + + } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Style/TextStyle.qml ausweisapp2-2.4.0/src/ui/qml/modules/Style/TextStyle.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Style/TextStyle.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Style/TextStyle.qml 2025-10-30 10:10:48.000000000 +0000 @@ -4,7 +4,7 @@ import QtQuick QtObject { - property int fontWeight: Font.Normal + property int fontWeight: Style.font.bold property real lineHeight: Style.dimens.lineHeight property color textColor: Style.color.textNormal.basic property real textSize: Style.dimens.text diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Style/internal/Colors.qml ausweisapp2-2.4.0/src/ui/qml/modules/Style/internal/Colors.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Style/internal/Colors.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Style/internal/Colors.qml 2025-10-30 10:10:48.000000000 +0000 @@ -25,7 +25,6 @@ required property ComponentColors linkTitle required property CoreComponents pane required property CoreComponents paneSublevel - required property color progressbar_text required property ComponentColors remoteIndicator property bool software_renderer: false required property color success diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Style/internal/ColorsDark.qml ausweisapp2-2.4.0/src/ui/qml/modules/Style/internal/ColorsDark.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Style/internal/ColorsDark.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Style/internal/ColorsDark.qml 2025-10-30 10:10:48.000000000 +0000 @@ -12,7 +12,6 @@ error: "#eb0000" focus_indicator: "#0098eb" image: "#0098eb" - progressbar_text: textNormal.basic success: "#5fcb01" warning: "#ff9b29" @@ -41,8 +40,9 @@ content.pressed: "#ffffff" } controlRadiobutton: DefaultControlColors { - background.pressed: "#0098eb" - content.basic: "#ffffff" + background.hovered: "#3d3d3d" + border.hovered: "#ffffff" + border.pressed: "#ffffff" } controlScrollbar: DefaultControlColors { background.basic: "#0098eb" diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Style/internal/ColorsLight.qml ausweisapp2-2.4.0/src/ui/qml/modules/Style/internal/ColorsLight.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Style/internal/ColorsLight.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Style/internal/ColorsLight.qml 2025-10-30 10:10:48.000000000 +0000 @@ -12,7 +12,6 @@ error: "#eb0000" focus_indicator: "#0077b6" image: "#0077b6" - progressbar_text: "#232323" success: "#3e8401" warning: "#db6a00" @@ -39,7 +38,11 @@ content.pressed: "#ffffff" } controlRadiobutton: DefaultControlColors { - background.pressed: "#0077b6" + background.hovered: "#f2f3f4" + border.basic: "#bcc0c1" + border.hovered: "#bcc0c1" + border.pressed: "#bcc0c1" + content.hovered: "#576164" } controlScrollbar: DefaultControlColors { background.basic: "#0077b6" diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Style/internal/TextStyles.qml ausweisapp2-2.4.0/src/ui/qml/modules/Style/internal/TextStyles.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Style/internal/TextStyles.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Style/internal/TextStyles.qml 2025-10-30 10:10:48.000000000 +0000 @@ -5,40 +5,43 @@ QtObject { readonly property var button: TextStyle { + fontWeight: Style.font.normal lineHeight: Style.dimens.lineHeight_button textColor: Style.color.control.content.basic } readonly property var headline: TextStyle { - fontWeight: Font.Medium + fontWeight: Style.font.medium lineHeight: Style.dimens.lineHeight_headline textColor: Style.color.textHeadline.basic textSize: Style.dimens.textHeadline } readonly property var link: TextStyle { + fontWeight: Style.font.normal textColor: Style.color.linkBasic.basic } readonly property var navigation: TextStyle { - fontWeight: Style.is_layout_desktop ? Font.Medium : Font.Normal + fontWeight: Style.is_layout_desktop ? Style.font.medium : Style.font.normal lineHeight: Style.dimens.lineHeight_navigation textColor: Style.color.linkNavigation.basic textSize: Style.dimens.text_navigation } readonly property var normal: TextStyle { + fontWeight: Style.font.normal } readonly property var subline: TextStyle { - fontWeight: Font.Medium + fontWeight: Style.font.medium lineHeight: Style.dimens.lineHeight_subline textColor: Style.color.textSubline.basic textSize: Style.dimens.textSubline } readonly property var tile: TextStyle { - fontWeight: Font.Medium + fontWeight: Style.font.medium lineHeight: Style.dimens.lineHeight_tile textColor: Style.color.textTitle.basic textSize: Style.dimens.text_tile } readonly property var title: TextStyle { - fontWeight: Font.Bold + fontWeight: Style.font.bold lineHeight: Style.dimens.lineHeight_title textColor: Style.color.textTitle.basic textSize: Style.dimens.textTitle diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+desktop/NavigationAction.qml ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+desktop/NavigationAction.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+desktop/NavigationAction.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+desktop/NavigationAction.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,19 +14,35 @@ enum Action { None, Cancel, - Back + Back, + Close } - readonly property color pressColor: Qt.darker(Style.color.textTitle.basic, Style.color.highlightDarkerFactor) property int type: NavigationAction.Action.Cancel colorStyle: Style.color.linkTitle height: if (parent) parent.height horizontalPadding: 0 - icon.source: root.type === NavigationAction.Action.Cancel ? "qrc:///images/material_close.svg" : "qrc:///images/material_arrow_back.svg" + icon.source: switch (root.type) { + case NavigationAction.Action.Cancel: + case NavigationAction.Action.Close: + return "qrc:///images/material_close.svg"; + default: + return "qrc:///images/material_arrow_back.svg"; + } iconSize: Style.dimens.small_icon_size * 1.5 - text: root.type === NavigationAction.Action.Cancel ? qsTr("Cancel") : qsTr("Back") + text: switch (root.type) { + case NavigationAction.Action.Cancel: + //: LABEL DESKTOP + return qsTr("Cancel"); + case NavigationAction.Action.Close: + //: LABEL DESKTOP + return qsTr("Close"); + default: + //: LABEL DESKTOP + return qsTr("Back"); + } tintIcon: true verticalPadding: 0 visible: ApplicationModel.currentWorkflow !== ApplicationModel.Workflow.NONE diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+desktop/TitleBar.qml ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+desktop/TitleBar.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+desktop/TitleBar.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+desktop/TitleBar.qml 2025-10-30 10:10:48.000000000 +0000 @@ -7,6 +7,7 @@ import Governikus.Global import Governikus.Style +import Governikus.UpdateView import Governikus.View import Governikus.Type @@ -18,21 +19,16 @@ property alias showPane: titlePane.visible readonly property string title: contentItem.title + signal showUpdate signal startClicked function setActiveFocus() { forceActiveFocus(Qt.MouseFocusReason); } - //: LABEL DESKTOP - Accessible.name: qsTr("Title bar") - Accessible.role: Accessible.Grouping - activeFocusOnTab: true color: Style.color.background height: titleBarColumn.height - FocusPoint { - } Column { id: titleBarColumn @@ -59,6 +55,15 @@ onClicked: root.startClicked() } + GLink { + anchors.bottom: parent.bottom + anchors.right: rightTitleBarActions.left + font.underline: true + text: qsTr("Update available") + " (%1)".arg(SettingsModel.appUpdateData.version) + visible: SettingsModel.appUpdateData.updateAvailable && !(root.contentItem instanceof UpdateView) + + onClicked: root.showUpdate() + } Row { id: rightTitleBarActions @@ -85,15 +90,21 @@ qsTr("Hide in-app notifications of %1").arg(Qt.application.name) : //: LABEL DESKTOP qsTr("Show in-app notifications of %1").arg(Qt.application.name) - Accessible.role: Accessible.CheckBox + Accessible.role: { + if ("Switch" in Accessible) { + return Accessible.Switch; // qmllint disable missing-property + } + return Accessible.CheckBox; + } checkable: true checked: notifications.visibleToUser height: rightTitleBarActions.height iconColor: notifications.iconColor source: notifications.unreadMessages ? "qrc:///images/desktop/notifications_on.svg" : "qrc:///images/desktop/notifications_off.svg" text: qsTr("Notifications") - visible: SettingsModel.showInAppNotifications + visible: SettingsModel.developerOptions && SettingsModel.showInAppNotifications + Accessible.onToggleAction: notifications.toggle() onClicked: notifications.toggle() } } @@ -125,7 +136,7 @@ Layout.leftMargin: Style.dimens.pane_padding enabled: root.currentSettings.navigationEnabled type: root.currentSettings.navigationAction - visible: root.currentSettings.navigationAction !== NavigationAction.None + visible: root.currentSettings.navigationAction !== NavigationAction.Action.None onClicked: root.currentSettings.navigationActionClicked() } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+desktop/TitleBarSettings.qml ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+desktop/TitleBarSettings.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+desktop/TitleBarSettings.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+desktop/TitleBarSettings.qml 2025-10-30 10:10:48.000000000 +0000 @@ -7,7 +7,7 @@ import Governikus.Type QtObject { - property int navigationAction: NavigationAction.None + property int navigationAction: NavigationAction.Action.None property bool navigationEnabled: true property bool showSettings: false property bool startEnabled: ApplicationModel.currentWorkflow === ApplicationModel.Workflow.NONE diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+desktop/internal/Notifications.qml ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+desktop/internal/Notifications.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+desktop/internal/Notifications.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+desktop/internal/Notifications.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,14 +14,7 @@ Item { id: root - readonly property color iconColor: { - if (d.unreadMsg) { - if (NotificationModel.lastType === "developermode") - return Style.color.warning; - return Style.color.textSubline.basic; - } - return Style.color.textNormal.basic; - } + readonly property color iconColor: d.unreadMsg ? Style.color.textSubline.basic : Style.color.textNormal.basic readonly property bool unreadMessages: d.unreadMsg readonly property bool visibleToUser: d.fadeIn && !fadeOutTimer.running @@ -61,9 +54,10 @@ border.color: Style.color.control.border.basic border.width: Style.dimens.border_width color: Style.color.control.background.basic - height: UiPluginModel.scaleFactor * 200 + height: UiPluginModel.scaleFactor * 120 radius: Style.dimens.control_radius - width: UiPluginModel.scaleFactor * 800 + visible: anchors.leftMargin < 0 + width: UiPluginModel.scaleFactor * 480 Behavior on anchors.leftMargin { PropertyAnimation { @@ -94,15 +88,19 @@ delegate: Item { id: logEntry + required property int index required property string text required property string time - required property string type Accessible.name: notificationTime.text + " " + notificationBody.text Accessible.role: Accessible.StaticText implicitHeight: row.height implicitWidth: row.width + 2 * Style.dimens.pane_padding + onActiveFocusChanged: if (activeFocus) { + logEntryList.handleItemFocused(index); + } + Row { id: row @@ -122,22 +120,22 @@ positionViewAtEndTimer.restart(); } root.newNotification(); + //: LABEL DESKTOP %1 will be replaced with a notification text + logEntry.Accessible.announce(qsTr("Notification: %1").arg(notificationBody.text)); } GText { id: notificationTime - activeFocusOnTab: false + Accessible.ignored: true color: Style.color.control.content.basic text: logEntry.time } GText { id: notificationBody - activeFocusOnTab: false + Accessible.ignored: true color: Style.color.control.content.basic - style: logEntry.type === "developermode" ? Text.Outline : Text.Normal - styleColor: Style.color.warning text: logEntry.text width: logEntryList.width - 2 * Style.dimens.pane_padding - notificationTime.width - 3 * logEntryList.spacing } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+desktop/internal/TitleBarButton.qml ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+desktop/internal/TitleBarButton.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+desktop/internal/TitleBarButton.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+desktop/internal/TitleBarButton.qml 2025-10-30 10:10:48.000000000 +0000 @@ -6,7 +6,6 @@ import Governikus.Global import Governikus.View import Governikus.Style -import Governikus.Type Button { id: root @@ -44,7 +43,7 @@ anchors.fill: parent anchors.margins: image.height / -4 - color: NotificationModel.lastType === "developermode" ? Style.color.warning : Style.color.green + color: Style.color.green opacity: 0 radius: height / 4 @@ -71,7 +70,11 @@ } } + Keys.onEscapePressed: tooltip.hide() + FocusFrame { + id: focusFrame + } MouseArea { id: mouseArea @@ -85,8 +88,19 @@ } } ToolTip { - delay: 500 + id: tooltip + + delay: Style.toolTipDelay text: root.text visible: mouseArea.containsMouse + + onAboutToHide: { + focusFrame.visible = true; + root.focus = false; + } + onAboutToShow: { + focusFrame.visible = false; + root.focus = true; + } } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+mobile/NavigationAction.qml ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+mobile/NavigationAction.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+mobile/NavigationAction.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+mobile/NavigationAction.qml 2025-10-30 10:10:48.000000000 +0000 @@ -7,7 +7,8 @@ enum Action { None, Cancel, - Back + Back, + Close } property int action: NavigationAction.Action.None diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+mobile/TitleBar.qml ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+mobile/TitleBar.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+mobile/TitleBar.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+mobile/TitleBar.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,7 +14,7 @@ property alias enableTileStyle: titlePane.visible property NavigationAction navigationAction - property var rightAction + property Component rightAction property alias showContent: contentLayout.visible property bool showSeparator: false property bool smartEidUsed: false @@ -28,6 +28,11 @@ color: Style.color.background height: Math.ceil((showContent ? contentLayout.implicitHeight : 0) + topSafeAreaMargin + titlePane.shadowHeight) + onRightActionChanged: rightActionLoader.setRightAction(rightAction) + + MouseArea { + anchors.fill: parent + } Rectangle { id: safeAreaBackground @@ -93,30 +98,37 @@ GSpacer { Layout.fillWidth: true } - Item { - id: rightActionStack - - property var actionItem: root.rightAction - property var activeActionItem + Loader { + id: rightActionLoader - Layout.alignment: Qt.AlignRight - children: activeActionItem ? [activeActionItem] : [] - implicitHeight: activeActionItem ? activeActionItem.implicitHeight : 0 - implicitWidth: activeActionItem ? activeActionItem.implicitWidth : 0 + function setRightAction(pRightAction) { + if (SettingsModel.useAnimations && pRightAction !== null) { + rightActionStackAnimateOut.newSourceComponent = pRightAction; + rightActionStackAnimateOut.start(); + return; + } + rightActionLoader.sourceComponent = pRightAction; + } - onActionItemChanged: rightActionStackAnimateOut.start() - onActiveActionItemChanged: rightActionStackAnimateIn.start() + Layout.alignment: Qt.AlignRight | Qt.AlignTop PropertyAnimation { id: rightActionStackAnimateOut + property Component newSourceComponent: null + duration: Style.animation_duration easing.type: Easing.InCubic property: "opacity" - target: rightActionStack + target: rightActionLoader.item to: 0 - onStopped: rightActionStack.activeActionItem = rightActionStack.actionItem + onStopped: { + rightActionLoader.sourceComponent = newSourceComponent; + if (newSourceComponent !== null) { + rightActionStackAnimateIn.start(); + } + } } PropertyAnimation { id: rightActionStackAnimateIn @@ -124,7 +136,7 @@ duration: Style.animation_duration easing.type: Easing.OutCubic property: "opacity" - target: rightActionStack + target: rightActionLoader.item to: 1 } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+mobile/TitleBarAction.qml ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+mobile/TitleBarAction.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+mobile/TitleBarAction.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+mobile/TitleBarAction.qml 2025-10-30 10:10:48.000000000 +0000 @@ -11,7 +11,7 @@ id: root Accessible.focusable: true - Accessible.ignored: icon.source == "" && text === "" + Accessible.ignored: icon.source.toString() === "" && text === "" Accessible.name: text Accessible.role: Accessible.Button activeFocusOnTab: !Accessible.ignored @@ -19,8 +19,6 @@ horizontalPadding: 0 textStyle: Style.text.navigation - Keys.onSpacePressed: clicked() - MouseArea { id: mouseArea diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+mobile/TitleBarNavigation.qml ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+mobile/TitleBarNavigation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/TitleBar/+mobile/TitleBarNavigation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/TitleBar/+mobile/TitleBarNavigation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -12,6 +12,7 @@ Accessible.name: text icon.source: { switch (root.navAction) { + case NavigationAction.Action.Close: case NavigationAction.Action.Cancel: return "qrc:///images/material_close.svg"; case NavigationAction.Action.Back: @@ -22,11 +23,14 @@ } text: { switch (root.navAction) { - //: LABEL ANDROID IOS + case NavigationAction.Action.Close: + //: LABEL ANDROID IOS + return qsTr("Close"); case NavigationAction.Action.Cancel: + //: LABEL ANDROID IOS return qsTr("Cancel"); - //: LABEL ANDROID IOS case NavigationAction.Action.Back: + //: LABEL ANDROID IOS return qsTr("Back"); default: return ""; diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/UpdateView/+desktop/UpdateView.qml ausweisapp2-2.4.0/src/ui/qml/modules/UpdateView/+desktop/UpdateView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/UpdateView/+desktop/UpdateView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/UpdateView/+desktop/UpdateView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -9,9 +9,7 @@ import QtQuick.Controls import QtQuick.Layouts -import Governikus.Animations import Governikus.Global -import Governikus.ResultView import Governikus.Style import Governikus.TitleBar import Governikus.Type @@ -20,15 +18,16 @@ FlickableSectionPage { id: root - property alias downloadRunning: updateButtons.downloadInProgress + readonly property bool downloadRunning: SettingsModel.appUpdateData.downloadRunning readonly property var update: SettingsModel.appUpdateData - fillWidth: true + spacing: Style.dimens.pane_spacing + //: LABEL DESKTOP title: qsTr("Application update") titleBarSettings: TitleBarSettings { - navigationAction: root.downloadRunning ? NavigationAction.Cancel : NavigationAction.Back + navigationAction: root.downloadRunning ? NavigationAction.Action.Cancel : NavigationAction.Action.Back onNavigationActionClicked: { if (root.downloadRunning) { @@ -38,95 +37,61 @@ } } - ResultView { - Layout.fillHeight: true + Subheading { + elide: Text.ElideRight + maximumLineCount: 1 + //: LABEL DESKTOP + text: qsTr("Update available") + } + GText { + //: LABEL DESKTOP %1 is replaced with the current version number + text: qsTr("An update for the outdated installed version (%1) is available for download.").arg(Qt.application.version) + } + UpdateViewInformation { + id: updateInformation + + Layout.fillWidth: true + downloadSize: root.update.size + releaseDate: root.update.date + version: root.update.version + } + UpdateViewButtonRow { + id: updateButtons + Layout.fillWidth: true - anchors.fill: null - animationSymbol: Symbol.Type.ERROR - animationType: root.update.missingPlatform ? AnimationLoader.Type.STATUS : AnimationLoader.Type.NETWORK_ERROR - showOkButton: false - text: root.update.missingPlatform ? - //: LABEL DESKTOP Resulttext if no update information is available for the current platform. - qsTr("An update information for your platform is not available.") : - //: LABEL DESKTOP Resulttext if the update information are invalid, might be caused by network issues. - qsTr("The update information could not be retrieved. Please check your network connection.") - title: root.title - visible: !root.update.valid + downloadInProgress: root.downloadRunning + downloadProgressKB: root.update.downloadProgress + downloadTotalKB: root.update.downloadTotal + version: root.update.version - onLeaveView: root.leaveView() + onAbortDownload: root.update.abortDownload() + onStartDownload: download.exec() } - ResultView { - Layout.fillHeight: true + GPane { Layout.fillWidth: true - anchors.fill: null - animationSymbol: Symbol.Type.CHECK - animationType: AnimationLoader.Type.STATUS - showOkButton: false - //: LABEL DESKTOP The currently installed version is the most recent one, no action is required. - text: qsTr("Your version %1 of %2 is up to date!").arg(Qt.application.version).arg(Qt.application.name) - title: root.title - visible: root.update.valid && !root.update.updateAvailable - - onLeaveView: root.leaveView() - } - ColumnLayout { - spacing: Style.dimens.pane_spacing - visible: root.update.valid && root.update.updateAvailable - - GText { - Layout.leftMargin: Style.dimens.pane_padding - Layout.rightMargin: Layout.leftMargin - elide: Text.ElideRight - maximumLineCount: 1 - text: qsTr("An update is available (installed version %1)").arg(Qt.application.version) - textStyle: Style.text.subline - } - UpdateViewInformation { - id: updateInformation - - Layout.fillWidth: true - Layout.leftMargin: Style.dimens.pane_padding - Layout.rightMargin: Layout.leftMargin - downloadSize: root.update.size - releaseDate: root.update.date - version: root.update.version - } - GSeparator { - Layout.fillWidth: true - } - UpdateViewButtonRow { - id: updateButtons - - Layout.fillWidth: true - Layout.leftMargin: Style.dimens.pane_padding - Layout.rightMargin: Layout.leftMargin - progressText: "%1 KiB / %2 KiB".arg(root.update.downloadProgress.toLocaleString(Qt.locale(SettingsModel.language), "f", 0)).arg(root.update.downloadTotal.toLocaleString(Qt.locale(SettingsModel.language), "f", 0)) - progressValue: root.update.downloadProgress * 100 / root.update.downloadTotal - - onToggleUpdate: root.downloadRunning ? root.update.abortDownload() : download.exec() - } - GSeparator { - Layout.fillWidth: true - } - GPane { - Layout.fillWidth: true - contentPadding: 0 - shadowScale: 1.005 - spacing: 0 + contentPadding: 0 + contentSpacing: 0 + shadowScale: 1.005 - ReleaseInformationModel { - id: releaseInformationModel + ReleaseInformationModel { + id: releaseInformationModel - } - Repeater { - id: releaseInfoRepeater + } + Repeater { + id: releaseInfoRepeater + + model: releaseInformationModel.updateRelease - model: releaseInformationModel.updateRelease + FormattedTextView { + Layout.fillWidth: true + color: Style.color.transparent + count: releaseInfoRepeater.count - FormattedTextView { - Layout.fillWidth: true - color: Style.color.transparent - totalItemCount: releaseInfoRepeater.count + onActiveFocusChanged: if (activeFocus) { + Utils.positionViewAtItem(this); + } + + FocusFrame { } } } @@ -135,9 +100,6 @@ function onFireAppDownloadFinished() { UiPluginModel.fireQuitApplicationRequest(); } - function onFireAppUpdateAborted() { - root.downloadRunning = false; - } function onFireAppUpdateFailed(pError, pSupportInfo) { warning.exec(pError, pSupportInfo); } @@ -154,7 +116,6 @@ open(); } function load() { - root.downloadRunning = true; root.update.download(); } @@ -176,7 +137,6 @@ id: warning function exec(pError, pSupportInfo) { - root.downloadRunning = false; text = pError; supportInfoText.text = pSupportInfo; open(); diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/UpdateView/+desktop/internal/UpdateViewButtonRow.qml ausweisapp2-2.4.0/src/ui/qml/modules/UpdateView/+desktop/internal/UpdateViewButtonRow.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/UpdateView/+desktop/internal/UpdateViewButtonRow.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/UpdateView/+desktop/internal/UpdateViewButtonRow.qml 2025-10-30 10:10:48.000000000 +0000 @@ -9,36 +9,55 @@ import Governikus.Type import Governikus.Style -RowLayout { +ColumnLayout { id: root - property bool downloadInProgress: false - property alias progressText: bar.text - property alias progressValue: bar.value - - signal toggleUpdate - - spacing: Style.dimens.pane_spacing - + required property bool downloadInProgress + required property int downloadProgressKB + required property int downloadTotalKB + required property string version + + signal abortDownload + signal startDownload + + GText { + //: LABEL DESKTOP %1 is replaced with the version number of the software update. + text: qsTr("The update (version %1) is being performed...").arg(root.version) + visible: root.downloadInProgress + } GProgressBar { id: bar + readonly property string localeDownloadedKB: root.downloadProgressKB.toLocaleString(Qt.locale(SettingsModel.language), "f", 0) + readonly property string localeTotalKB: root.downloadTotalKB.toLocaleString(Qt.locale(SettingsModel.language), "f", 0) + + //: LABEL DESKTOP Name of an progress indicator during a download read by screen readers + Accessible.name: qsTr("Download progress") Layout.fillWidth: true - activeFocusOnTab: true + text: "%1 %".arg(Math.floor(bar.value)) + value: root.downloadProgressKB * 100 / root.downloadTotalKB visible: root.downloadInProgress } - GSpacer { - Layout.fillWidth: true - visible: !root.downloadInProgress - } GButton { enabledTooltipText: SettingsModel.appUpdateData.url - text: root.downloadInProgress ? - //: LABEL DESKTOP Cancel the download of the update on Windows - qsTr("Cancel update") : //: LABEL DESKTOP Start to download the update and execute it on Windows - qsTr("Start update") + text: qsTr("Start update") + visible: !root.downloadInProgress + + onActiveFocusChanged: if (activeFocus) + Utils.positionViewAtItem(this) + onClicked: root.startDownload() + } + GLink { + colorStyle: Style.color.linkTitle + font.underline: true + horizontalPadding: 0 + //: LABEL DESKTOP Cancel the download of the update on Windows + text: qsTr("Cancel update") + visible: root.downloadInProgress - onClicked: root.toggleUpdate() + onActiveFocusChanged: if (activeFocus) + Utils.positionViewAtItem(this) + onClicked: root.abortDownload() } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/UpdateView/+desktop/internal/UpdateViewInformation.qml ausweisapp2-2.4.0/src/ui/qml/modules/UpdateView/+desktop/internal/UpdateViewInformation.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/UpdateView/+desktop/internal/UpdateViewInformation.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/UpdateView/+desktop/internal/UpdateViewInformation.qml 2025-10-30 10:10:48.000000000 +0000 @@ -25,31 +25,35 @@ columns: 2 GText { + font.weight: Style.font.bold //: LABEL DESKTOP Information about the available, new version number. - text: qsTr("New version:") + text: qsTr("New version") } GText { id: textVersion } GText { + font.weight: Style.font.bold //: LABEL DESKTOP Date when the available update was released. - text: qsTr("Release date:") + text: qsTr("Release date") } GText { text: root.releaseDate.toLocaleDateString(Qt.locale(SettingsModel.language)) } GText { + font.weight: Style.font.bold //: LABEL DESKTOP Download size of the available update in megabyte. - text: qsTr("Download size:") + text: qsTr("Download size") } GText { - text: "%1 MiB".arg((root.downloadSize / 1024576).toLocaleString(Qt.locale(SettingsModel.language), "f", 1)) + text: "%1 MB".arg((root.downloadSize / 1000000).toLocaleString(Qt.locale(SettingsModel.language), "f", 1)) } GText { + font.weight: Style.font.bold //: LABEL DESKTOP Plaintext link to the update download. - text: qsTr("Download link:") + text: qsTr("Download link") visible: root.downloadUrl !== "" } GText { @@ -57,8 +61,9 @@ visible: root.downloadUrl !== "" } GText { + font.weight: Style.font.bold //: LABEL DESKTOP Link to download checksum to verify the downloaded update file. - text: qsTr("Checksum link:") + text: qsTr("Checksum link") visible: root.checksumUrl !== "" } GText { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/View/+desktop/ContentArea.qml ausweisapp2-2.4.0/src/ui/qml/modules/View/+desktop/ContentArea.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/View/+desktop/ContentArea.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/View/+desktop/ContentArea.qml 2025-10-30 10:10:48.000000000 +0000 @@ -44,11 +44,15 @@ push(moreView); break; case UiModule.IDENTIFY: - if (ApplicationModel.currentWorkflow === ApplicationModel.Workflow.NONE) { + switch (ApplicationModel.currentWorkflow) { + case ApplicationModel.Workflow.NONE: + pModule = UiModule.SELF_AUTHENTICATION; push(selfAuthView); - } - if (ApplicationModel.currentWorkflow === ApplicationModel.Workflow.AUTHENTICATION || ApplicationModel.currentWorkflow === ApplicationModel.Workflow.SELF_AUTHENTICATION) { + break; + case ApplicationModel.Workflow.AUTHENTICATION: + case ApplicationModel.Workflow.SELF_AUTHENTICATION: push(authView); + break; } break; case UiModule.ONBOARDING: @@ -64,7 +68,6 @@ requestedModule = pModule; } - Accessible.ignored: Qt.platform.os === "windows" popEnter: null popExit: null pushEnter: null diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/View/+desktop/FocusPoint.qml ausweisapp2-2.4.0/src/ui/qml/modules/View/+desktop/FocusPoint.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/View/+desktop/FocusPoint.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/View/+desktop/FocusPoint.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany - */ - -import QtQuick - -import Governikus.Style -import Governikus.Type - -Text { - id: root - - property Item scope: parent - - anchors.left: parent.left - anchors.top: parent.top - color: Style.color.focus_indicator - font.pixelSize: Style.dimens.text - horizontalAlignment: Text.AlignHCenter - text: "✱" - visible: scope.activeFocus && UiPluginModel.showFocusIndicator - width: height -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/View/+desktop/SectionPage.qml ausweisapp2-2.4.0/src/ui/qml/modules/View/+desktop/SectionPage.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/View/+desktop/SectionPage.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/View/+desktop/SectionPage.qml 2025-10-30 10:10:48.000000000 +0000 @@ -63,13 +63,6 @@ } Component.onCompleted: setActive() - Keys.onEscapePressed: event => { - if (titleBarSettings.navigationAction === NavigationAction.Back && titleBarSettings.navigationEnabled) { - titleBarSettings.navigationActionClicked(); - } else { - event.accepted = false; - } - } onVisibleChanged: setActive() Timer { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/View/+mobile/ContentArea.qml ausweisapp2-2.4.0/src/ui/qml/modules/View/+mobile/ContentArea.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/View/+mobile/ContentArea.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/View/+mobile/ContentArea.qml 2025-10-30 10:10:48.000000000 +0000 @@ -52,6 +52,7 @@ visible: root.activeModule === UiModule.DEFAULT initialItem: MainView { + skipFocusUpdate: true } } TabBarView { @@ -117,6 +118,7 @@ visible: root.activeModule === UiModule.REMOTE_SERVICE initialItem: RemoteServiceView { + skipFocusUpdate: true } } TabBarView { @@ -126,6 +128,7 @@ visible: root.activeModule === UiModule.SETTINGS initialItem: SettingsView { + skipFocusUpdate: true } } TabBarView { @@ -135,6 +138,8 @@ visible: root.activeModule === UiModule.HELP initialItem: MoreView { + skipFocusUpdate: true + onStartOnboarding: show(UiModule.ONBOARDING, true) } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/View/+mobile/FocusPoint.qml ausweisapp2-2.4.0/src/ui/qml/modules/View/+mobile/FocusPoint.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/View/+mobile/FocusPoint.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/View/+mobile/FocusPoint.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -/** - * Copyright (c) 2019-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick - -Text { - property Item scope: parent - - visible: false -} diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/View/+mobile/SectionPage.qml ausweisapp2-2.4.0/src/ui/qml/modules/View/+mobile/SectionPage.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/View/+mobile/SectionPage.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/View/+mobile/SectionPage.qml 2025-10-30 10:10:48.000000000 +0000 @@ -11,14 +11,14 @@ property bool enableTileStyle: true property var navigationAction: null - property var rightTitleBarAction: null + property Component rightTitleBarAction: null property bool showTitleBarContent: true property bool smartEidUsed: false required property string title Connections { function onActivate() { - if (ApplicationModel.isScreenReaderRunning) { + if (ApplicationModel.screenReaderRunning) { root.updateFocus(); } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/View/+mobile/TabBarView.qml ausweisapp2-2.4.0/src/ui/qml/modules/View/+mobile/TabBarView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/View/+mobile/TabBarView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/View/+mobile/TabBarView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -13,7 +13,7 @@ StackView { id: root - readonly property bool animationEnabled: !ApplicationModel.isScreenReaderRunning && SettingsModel.useAnimations + readonly property bool animationEnabled: !ApplicationModel.screenReaderRunning && SettingsModel.useAnimations function doActivate() { if (visible && currentItem && (currentItem as BaseController)) { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/View/FlickableSectionPage.qml ausweisapp2-2.4.0/src/ui/qml/modules/View/FlickableSectionPage.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/View/FlickableSectionPage.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/View/FlickableSectionPage.qml 2025-10-30 10:10:48.000000000 +0000 @@ -13,8 +13,8 @@ property real margins: Style.dimens.pane_padding property alias spacing: flickable.spacing - function positionViewAtItem(pItem) { - flickable.positionViewAtItem(pItem); + function positionViewAtItem(pItem, pPositionItemAtMiddle = false) { + Utils.positionFlickableAtItem(flickable, pItem, pPositionItemAtMiddle); } function scrollPageDown() { flickable.scrollPageDown(); @@ -26,7 +26,7 @@ contentIsScrolled: !flickable.atYBeginning Keys.onPressed: event => { - flickable.handleKeyPress(event.key); + flickable.handleKeyPress(event); } Connections { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/View/FocusFrame.qml ausweisapp2-2.4.0/src/ui/qml/modules/View/FocusFrame.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/View/FocusFrame.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/View/FocusFrame.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,12 +14,18 @@ property Item framee: parent property real marginFactor: 1 property Item scope: parent - property real size: Math.max(UiPluginModel.scaleFactor * 4, 1) + property real size: Style.dimens.border_width * 2 anchors.fill: framee anchors.margins: marginFactor * -size * 2 border.color: root.borderColor - border.width: scope.activeFocus && UiPluginModel.showFocusIndicator ? size : 0 + border.width: { + if (Qt.platform.os !== "windows" && ApplicationModel.screenReaderRunning) + return 0; + if (scope.activeFocus && UiPluginModel.showFocusIndicator) + return size; + return 0; + } color: Style.color.transparent radius: Math.min(height / 4, Style.dimens.control_radius) } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/View/internal/BaseController.qml ausweisapp2-2.4.0/src/ui/qml/modules/View/internal/BaseController.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/View/internal/BaseController.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/View/internal/BaseController.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,10 +1,11 @@ /** * Copyright (c) 2021-2025 Governikus GmbH & Co. KG, Germany */ + import QtQuick import QtQuick.Controls + import Governikus.TitleBar -import Governikus.Type import Governikus.Global Item { @@ -12,32 +13,13 @@ property bool contentIsScrolled: false property ProgressTracker progress: null + property bool skipFocusUpdate: false signal activate signal leaveView - function showRemoveCardFeedback(workflowModel, success) { - if (workflowModel.showRemoveCardFeedback) { - workflowModel.showRemoveCardFeedback = false; - if (Qt.platform.os === "ios") { - // 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 will not show the notification during an authentication on iOS with VoiceOver running. - if (ApplicationModel.isScreenReaderRunning && ApplicationModel.currentWorkflow === ApplicationModel.Workflow.AUTHENTICATION) { - return; - } - } - if (success) { - //: INFO ALL_PLATFORMS The workflow finished successfully, the ID card may (and should) be removed from the card reader. - ApplicationModel.showFeedback(qsTr("Process finished successfully. You may now remove your ID card from the device.")); - } else { - //: INFO ALL_PLATFORMS The workflow 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 updateFocus() { - if (!visible) { + if (!visible || skipFocusUpdate) { return; } if (d.forceFocusFirstA11yItem(root)) { diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+desktop/GeneralWorkflow.qml ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+desktop/GeneralWorkflow.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+desktop/GeneralWorkflow.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+desktop/GeneralWorkflow.qml 2025-10-30 10:10:48.000000000 +0000 @@ -26,14 +26,14 @@ QtObject { id: d - readonly property bool foundPCSCReader: ApplicationModel.availableReader > 0 && ApplicationModel.isReaderTypeAvailable(ReaderManagerPluginType.PCSC) - readonly property bool foundRemoteReader: ApplicationModel.availableReader > 0 && ApplicationModel.isReaderTypeAvailable(ReaderManagerPluginType.REMOTE_IFD) - readonly property bool foundSelectedReader: ApplicationModel.availableReader > 0 + readonly property bool foundPCSCReader: ApplicationModel.availablePcscReader > 0 && (ApplicationModel.usedPluginType === ReaderManagerPluginType.UNKNOWN || ApplicationModel.usedPluginType === ReaderManagerPluginType.PCSC) + readonly property bool foundRemoteReader: ApplicationModel.availableRemoteReader > 0 && (ApplicationModel.usedPluginType === ReaderManagerPluginType.UNKNOWN || ApplicationModel.usedPluginType === ReaderManagerPluginType.REMOTE_IFD) + readonly property bool foundSelectedReader: foundPCSCReader || foundRemoteReader readonly property bool showProgressIndicator: root.progress === null || !root.progress.enabled - onFoundPCSCReaderChanged: if (ApplicationModel.isScreenReaderRunning) + onFoundPCSCReaderChanged: if (root.visible && ApplicationModel.screenReaderRunning) subText.forceActiveFocus() - onFoundRemoteReaderChanged: if (ApplicationModel.isScreenReaderRunning) + onFoundRemoteReaderChanged: if (root.visible && ApplicationModel.screenReaderRunning) subText.forceActiveFocus() } Connections { @@ -45,25 +45,44 @@ target: RemoteServiceModel } + ReaderDetection { + enabled: ApplicationModel.screenReaderRunning + + onNewPcscReaderDetected: root.push(readerFoundConfirmation, { + type: ReaderFoundConfirmation.ReaderType.PCSC + }) + onNewRemoteReaderDetected: root.push(readerFoundConfirmation, { + type: ReaderFoundConfirmation.ReaderType.REMOTE + }) + } + Component { + id: readerFoundConfirmation + + ReaderFoundConfirmation { + title: root.title + + onLeaveView: root.pop() + } + } AnimationLoader { symbol: root.waitingFor === Workflow.WaitingFor.Reader ? Symbol.Type.QUESTION : Symbol.Type.INFO type: { switch (root.waitingFor) { case Workflow.WaitingFor.Reader: if (d.foundRemoteReader) { - return AnimationLoader.WAIT_FOR_CARD_SAC; + return AnimationLoader.Type.WAIT_FOR_CARD_SAC; } if (d.foundPCSCReader) { - return AnimationLoader.WAIT_FOR_CARD_USB; + return AnimationLoader.Type.WAIT_FOR_CARD_USB; } - return AnimationLoader.WAIT_FOR_READER; + return AnimationLoader.Type.WAIT_FOR_READER; case Workflow.WaitingFor.Password: if (d.foundRemoteReader) { - return AnimationLoader.WAIT_FOR_SAC; + return AnimationLoader.Type.WAIT_FOR_SAC; } - return AnimationLoader.WAIT_FOR_READER; + return AnimationLoader.Type.WAIT_FOR_READER; default: - return AnimationLoader.NONE; + return AnimationLoader.Type.NONE; } } @@ -95,10 +114,9 @@ topMargin: 3 * Style.dimens.pane_spacing + Style.dimens.header_icon_size } } - GText { + Heading { id: mainText - horizontalAlignment: Text.AlignHCenter text: { switch (root.waitingFor) { case Workflow.WaitingFor.Reader: @@ -111,7 +129,7 @@ } return d.foundSelectedReader ? //: LABEL DESKTOP - qsTr("Place ID card") : + qsTr("Read out ID card with connected device") : //: LABEL DESKTOP qsTr("Connect USB card reader or smartphone"); case Workflow.WaitingFor.Password: @@ -121,7 +139,6 @@ return ""; } } - textStyle: Style.text.headline visible: text !== "" width: Math.min(parent.width - (2 * Style.dimens.pane_padding), Style.dimens.max_text_width) @@ -192,7 +209,7 @@ required property string deviceName animationSymbol: Symbol.Type.ERROR - animationType: AnimationLoader.SAC_RESULT + animationType: AnimationLoader.Type.SAC_RESULT //: INFO DESKTOP 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. text: qsTr("The device \"%1\" was unpaired because it did not react to connection attempts. Pair the device again to use it as a card reader.").arg(deviceName) title: root.title diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+desktop/internal/ProgressCircle.qml ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+desktop/internal/ProgressCircle.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+desktop/internal/ProgressCircle.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+desktop/internal/ProgressCircle.qml 2025-10-30 10:10:48.000000000 +0000 @@ -64,7 +64,7 @@ QtObject { id: d - readonly property int stepWidth: UiPluginModel.scaleFactor * 250 + readonly property int stepWidth: UiPluginModel.scaleFactor * 150 } Rectangle { id: line1 diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/GeneralWorkflow.qml ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/GeneralWorkflow.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/GeneralWorkflow.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/GeneralWorkflow.qml 2025-10-30 10:10:48.000000000 +0000 @@ -73,7 +73,7 @@ property string deviceName animationSymbol: Symbol.Type.ERROR - animationType: AnimationLoader.SAC_RESULT + animationType: AnimationLoader.Type.SAC_RESULT //: 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. text: qsTr("The device \"%1\" was unpaired because it did not react to connection attempts. Pair the device again to use it as a card reader.").arg(deviceName) title: root.workflowTitle diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/NfcConnectionInfoView.qml ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/NfcConnectionInfoView.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/NfcConnectionInfoView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/NfcConnectionInfoView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -52,38 +52,38 @@ ColumnLayout { spacing: Style.dimens.groupbox_spacing - GText { + Subheading { //: INFO ANDROID IOS text: qsTr("NFC interface") - textStyle: Style.text.subline } GText { //: INFO ANDROID IOS text: qsTr("The NFC interface, and therefore the point at which the ID card must be placed, varies depending on the smartphone model. Find out where the NFC interface is on your smartphone model.") } GButton { + readonly property string nfcInfoUrl: { + if (Qt.platform.os === "ios") + return "https://www.ausweisapp.bund.de/%1/aa2/video-nfc-ios".arg(SettingsModel.language); + return "https://www.ausweisapp.bund.de/%1/aa2/video-nfc-android".arg(SettingsModel.language); + } + + Accessible.description: Utils.platformAgnosticLinkOpenText(nfcInfoUrl, Accessible.name) + Accessible.role: Accessible.Link Layout.alignment: Qt.AlignHCenter icon.source: "qrc:///images/open_website.svg" //: LABEL ANDROID IOS text: qsTr("Tutorial: Using NFC") tintIcon: true - onClicked: { - if (Qt.platform.os === "ios") { - Qt.openUrlExternally("https://www.ausweisapp.bund.de/%1/aa2/video-nfc-ios".arg(SettingsModel.language)); - } else { - Qt.openUrlExternally("https://www.ausweisapp.bund.de/%1/aa2/video-nfc-android".arg(SettingsModel.language)); - } - } + onClicked: Qt.openUrlExternally(nfcInfoUrl) } } ColumnLayout { spacing: Style.dimens.groupbox_spacing - GText { + Subheading { //: INFO ANDROID IOS text: qsTr("Sources of interference") - textStyle: Style.text.subline } GText { text: qsTr("Remove your phone case and allow the ID card to come into direct contact with the device. Low battery power can negatively impact NFC functionality, so make sure your smartphone is charged and not in power saving mode.") @@ -92,10 +92,9 @@ ColumnLayout { spacing: Style.dimens.groupbox_spacing - GText { + Subheading { //: INFO ANDROID IOS text: qsTr("Alternatives") - textStyle: Style.text.subline } GText { //: INFO ANDROID IOS diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/NfcWorkflow.qml ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/NfcWorkflow.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/NfcWorkflow.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/NfcWorkflow.qml 2025-10-30 10:10:48.000000000 +0000 @@ -17,7 +17,8 @@ property bool cardInitiallyAppeared: false property bool isRemoteWorkflow: ApplicationModel.currentWorkflow === ApplicationModel.Workflow.REMOTE_SERVICE - readonly property int nfcState: visible ? ApplicationModel.nfcState : ApplicationModel.NfcState.UNAVAILABLE + readonly property int nfcState: visible ? ApplicationModel.nfcState : nfcStateInvisible + readonly property int nfcStateInvisible: -1 signal showNfcInformation signal showRemoteServiceSettings @@ -64,6 +65,8 @@ } return ""; + default: + return ""; } } infoText: { @@ -90,6 +93,8 @@ return "%1%2".arg(root.isRemoteWorkflow ? technologyInfo.positionHint + ".

" : "").arg( //: INFO ANDROID Text that one ID card position should be kept for several seconds qsTr("Keep one position for several seconds before trying another one and do not move the ID card after contact was established.")); + default: + return ""; } } showAdditionalContent: { @@ -119,7 +124,9 @@ return qsTr("NFC is switched off"); } else if (root.nfcState === ApplicationModel.NfcState.INACTIVE) { //: INFO ANDROID IOS NFC is available and enabled but needs to be started. - return qsTr("NFC scan is not running"); + return qsTr("Start scan"); + } else if (root.nfcState === root.nfcStateInvisible) { + return ""; } if (AuthModel.eidTypeMismatchError !== "") { return AuthModel.eidTypeMismatchError; @@ -136,7 +143,7 @@ return qsTr("NFC is disabled"); case ApplicationModel.NfcState.INACTIVE: //: INFO ANDROID IOS - return qsTr("Start scan"); + return qsTr("NFC scan is not running"); case ApplicationModel.NfcState.READY: if (ApplicationModel.extendedLengthApdusUnsupported) { //: INFO ANDROID IOS @@ -148,6 +155,8 @@ } //: INFO ANDROID IOS return qsTr("Read ID card"); + default: + return ""; } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/RemoteWorkflow.qml ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/RemoteWorkflow.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/RemoteWorkflow.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/RemoteWorkflow.qml 2025-10-30 10:10:48.000000000 +0000 @@ -14,7 +14,7 @@ GFlickableColumnLayout { id: root - property bool foundSelectedReader: ApplicationModel.availableReader > 0 + property bool foundSelectedReader: ApplicationModel.availableRemoteReader > 0 property bool wifiEnabled: ApplicationModel.wifiEnabled signal deviceUnpaired(var pDeviceName) diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/SimulatorWorkflow.qml ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/SimulatorWorkflow.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/SimulatorWorkflow.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/SimulatorWorkflow.qml 2025-10-30 10:10:48.000000000 +0000 @@ -77,7 +77,7 @@ style: Style.color.controlOptional //: LABEL ANDROID IOS text: qsTr("SMART") - visible: ApplicationModel.isSmartSupported + visible: ApplicationModel.smartSupported onClicked: root.workflowModel.readerPluginType = ReaderManagerPluginType.SMART } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/SmartWorkflow.qml ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/SmartWorkflow.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/SmartWorkflow.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/SmartWorkflow.qml 2025-10-30 10:10:48.000000000 +0000 @@ -68,7 +68,7 @@ subTitleText: { if (root.canUseSmart && !root.autoInsertCard) { //: LABEL ANDROID IOS - return qsTr("Your Smart-eID is ready for use, press \"Continue\" to proceed."); + return qsTr("Your Smart-eID is ready for use, tap \"Continue\" to proceed."); } return ""; } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/internal/RemoteProgressIndicator.qml ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/internal/RemoteProgressIndicator.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/internal/RemoteProgressIndicator.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/internal/RemoteProgressIndicator.qml 2025-10-30 10:10:48.000000000 +0000 @@ -20,6 +20,6 @@ AnimationLoader { anchors.centerIn: parent animated: root.foundSelectedReader - type: root.foundSelectedReader ? AnimationLoader.WAIT_FOR_CARD_SAC : AnimationLoader.WAIT_FOR_SAC + type: root.foundSelectedReader ? AnimationLoader.Type.WAIT_FOR_CARD_SAC : AnimationLoader.Type.WAIT_FOR_SAC } } diff -Nru ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/internal/TechnologyInfo.qml ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/internal/TechnologyInfo.qml --- ausweisapp2-2.3.1/src/ui/qml/modules/Workflow/+mobile/internal/TechnologyInfo.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/ui/qml/modules/Workflow/+mobile/internal/TechnologyInfo.qml 2025-10-30 10:10:48.000000000 +0000 @@ -25,12 +25,9 @@ Layout.rightMargin: Style.dimens.pane_padding spacing: Style.dimens.pane_spacing - GText { + Heading { id: title - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - textStyle: Style.text.headline visible: text !== "" } Loader { @@ -42,10 +39,9 @@ Layout.minimumHeight: item ? (item as Item).Layout.minimumHeight : -1 Layout.preferredHeight: item ? (item as Item).Layout.preferredHeight : -1 } - GText { + Subheading { id: subTitle - textStyle: Style.text.subline visible: text !== "" } GText { diff -Nru ausweisapp2-2.3.1/src/workflows/base/Survey.cpp ausweisapp2-2.4.0/src/workflows/base/Survey.cpp --- ausweisapp2-2.3.1/src/workflows/base/Survey.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/Survey.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -66,15 +66,15 @@ void Survey::buildDataObject() { 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("OS version"), mOsVersion); - mData += qMakePair(tr("Kernel version"), mKernelVersion); - mData += qMakePair(tr("Max. NFC Packet Length"), QString::number(mMaximumNfcPacketLength)); - mData += qMakePair(tr("%1 Version").arg(QCoreApplication::applicationName()), mAusweisAppVersionNumber); - mData += qMakePair(tr("NFC Tag Type"), mNfcTagType); + mData += std::make_pair(tr("Vendor"), mVendor); + mData += std::make_pair(tr("Model Name"), mModelName); + mData += std::make_pair(tr("Model Number"), mModelNumber); + mData += std::make_pair(tr("Build Number"), mBuildNumber); + mData += std::make_pair(tr("OS version"), mOsVersion); + mData += std::make_pair(tr("Kernel version"), mKernelVersion); + mData += std::make_pair(tr("Max. NFC Packet Length"), QString::number(mMaximumNfcPacketLength)); + mData += std::make_pair(tr("%1 Version").arg(QCoreApplication::applicationName()), mAusweisAppVersionNumber); + mData += std::make_pair(tr("NFC Tag Type"), mNfcTagType); Q_EMIT fireSurveyDataChanged(); } diff -Nru ausweisapp2-2.3.1/src/workflows/base/Survey.h ausweisapp2-2.4.0/src/workflows/base/Survey.h --- ausweisapp2-2.3.1/src/workflows/base/Survey.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/Survey.h 2025-10-30 10:10:48.000000000 +0000 @@ -4,14 +4,18 @@ #pragma once +#include "Env.h" +#include "ReaderInfo.h" + #include -#include #include -#include "ReaderInfo.h" +#include + class test_Survey; + namespace governikus { @@ -23,7 +27,7 @@ friend class ::test_Survey; public: - using SurveyData = QList>; + using SurveyData = QList>; private: const QString mBuildNumber; @@ -73,4 +77,5 @@ void fireSurveyDataChanged(); }; + } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/workflows/base/WorkflowRequest.h ausweisapp2-2.4.0/src/workflows/base/WorkflowRequest.h --- ausweisapp2-2.3.1/src/workflows/base/WorkflowRequest.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/WorkflowRequest.h 2025-10-30 10:10:48.000000000 +0000 @@ -6,7 +6,6 @@ #include "context/WorkflowContext.h" -#include #include #include @@ -48,7 +47,7 @@ return QSharedPointer::create(std::forward(pArgs) ...); }; - return qMakePair(controller, context); + return std::make_pair(controller, context); } public: diff -Nru ausweisapp2-2.3.1/src/workflows/base/context/AuthContext.cpp ausweisapp2-2.4.0/src/workflows/base/context/AuthContext.cpp --- ausweisapp2-2.3.1/src/workflows/base/context/AuthContext.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/context/AuthContext.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,13 +6,29 @@ #include "AppSettings.h" #include "SecureStorage.h" -#include "asn1/Chat.h" -#include "paos/retrieve/DidAuthenticateEac1Parser.h" +using namespace Qt::Literals::StringLiterals; using namespace governikus; +Q_DECLARE_LOGGING_CATEGORY(card) + + +const QList>& AuthContext::logCertificates(const QString& pSource, const QList>& pCertificates) const +{ + if (!pCertificates.isEmpty()) + { + qCDebug(card) << " Source:" << pSource; + for (const auto& cvc : pCertificates) + { + qCDebug(card) << " " << cvc; + } + } + return pCertificates; +} + + AuthContext::AuthContext(const Action pAction, bool pActivateUi, const QUrl& pActivationUrl, const BrowserHandler& pHandler) : WorkflowContext(pAction, pActivateUi) , mTcTokenNotFound(true) @@ -104,6 +120,18 @@ } +const QByteArray& AuthContext::getSslSessionPsk() const +{ + return mSslSessionPsk; +} + + +void AuthContext::setSslSessionPsk(const QByteArray& pSession) +{ + mSslSessionPsk = pSession; +} + + QByteArray AuthContext::encodeEffectiveChat() { if (!mAccessRightManager) @@ -157,12 +185,14 @@ { Q_ASSERT(mDIDAuthenticateEAC1); + qCDebug(card) << "Initialize ChainBuilder with certificates"; + QList> cvcs; - cvcs += CVCertificate::fromRaw(Env::getSingleton()->getPreVerificationSettings().getLinkCertificates()); - cvcs += getDidAuthenticateEac1()->getCvCertificates(); - cvcs += pAdditionalCertificates; + cvcs += logCertificates("PreVerification"_L1, CVCertificate::fromRaw(Env::getSingleton()->getPreVerificationSettings().getLinkCertificates())); + cvcs += logCertificates("Eac1"_L1, getDidAuthenticateEac1()->getCvCertificates()); + cvcs += logCertificates("Eac2"_L1, pAdditionalCertificates); const auto* secureStorage = Env::getSingleton(); - mCvcChainBuilderProd = CVCertificateChainBuilder(cvcs + CVCertificate::fromRaw(secureStorage->getCVRootCertificates(true)), true); - mCvcChainBuilderTest = CVCertificateChainBuilder(cvcs + CVCertificate::fromRaw(secureStorage->getCVRootCertificates(false)), false); + mCvcChainBuilderProd = CVCertificateChainBuilder(cvcs + logCertificates("Productive"_L1, CVCertificate::fromRaw(secureStorage->getCVRootCertificates(true))), true); + mCvcChainBuilderTest = CVCertificateChainBuilder(cvcs + logCertificates("Test"_L1, CVCertificate::fromRaw(secureStorage->getCVRootCertificates(false))), false); } diff -Nru ausweisapp2-2.3.1/src/workflows/base/context/AuthContext.h ausweisapp2-2.4.0/src/workflows/base/context/AuthContext.h --- ausweisapp2-2.3.1/src/workflows/base/context/AuthContext.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/context/AuthContext.h 2025-10-30 10:10:48.000000000 +0000 @@ -4,7 +4,6 @@ #pragma once -#include "NetworkManager.h" #include "TcToken.h" #include "UrlUtil.h" #include "asn1/CVCertificate.h" @@ -31,10 +30,12 @@ #include + class test_StateRedirectBrowser; class test_StatePreVerification; class test_StateCertificateDescriptionCheck; + namespace governikus { class TestAuthContext; @@ -82,12 +83,16 @@ CVCertificateChainBuilder mCvcChainBuilderProd; CVCertificateChainBuilder mCvcChainBuilderTest; QByteArray mSslSession; + QByteArray mSslSessionPsk; BrowserHandler mBrowserHandler; + const QList>& logCertificates(const QString& pSource, const QList>& pCertificates) const; + Q_SIGNALS: void fireShowChangePinViewChanged(); void fireDidAuthenticateEac1Changed(); void fireAccessRightManagerCreated(QSharedPointer pAccessRightManager); + void fireRefreshUrlChanged(); protected: explicit AuthContext(const Action pAction, bool pActivateUi = true, const QUrl& pActivationUrl = QUrl(), const BrowserHandler& pHandler = BrowserHandler()); @@ -230,7 +235,11 @@ void setRefreshUrl(const QUrl& pRefreshUrl) { - mRefreshUrl = pRefreshUrl; + if (mRefreshUrl != pRefreshUrl) + { + mRefreshUrl = pRefreshUrl; + Q_EMIT fireRefreshUrlChanged(); + } } @@ -421,6 +430,10 @@ [[nodiscard]] const QByteArray& getSslSession() const; void setSslSession(const QByteArray& pSession); + [[nodiscard]] const QByteArray& getSslSessionPsk() const; + void setSslSessionPsk(const QByteArray& pSession); + }; + } // namespace governikus diff -Nru ausweisapp2-2.3.1/src/workflows/base/context/ChangePinContext.cpp ausweisapp2-2.4.0/src/workflows/base/context/ChangePinContext.cpp --- ausweisapp2-2.3.1/src/workflows/base/context/ChangePinContext.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/context/ChangePinContext.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -8,7 +8,7 @@ ChangePinContext::ChangePinContext(bool pRequestTransportPin, bool pActivateUi, bool pOnlyCheckPin) - : WorkflowContext(Action::PIN, pActivateUi) + : WorkflowContext(Action::CHANGE_PIN, pActivateUi) , mNewPin() , mSuccessMessage() , mRequestTransportPin(pRequestTransportPin) diff -Nru ausweisapp2-2.3.1/src/workflows/base/context/WorkflowContext.h ausweisapp2-2.4.0/src/workflows/base/context/WorkflowContext.h --- ausweisapp2-2.3.1/src/workflows/base/context/WorkflowContext.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/context/WorkflowContext.h 2025-10-30 10:10:48.000000000 +0000 @@ -20,8 +20,8 @@ { defineEnumType(Action, AUTH, - SELF, - PIN, + SELF_AUTH, + CHANGE_PIN, PERSONALIZATION, REMOTE_SERVICE) diff -Nru ausweisapp2-2.3.1/src/workflows/base/controller/AuthController.cpp ausweisapp2-2.4.0/src/workflows/base/controller/AuthController.cpp --- ausweisapp2-2.3.1/src/workflows/base/controller/AuthController.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/controller/AuthController.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -59,7 +59,7 @@ QSharedPointer AuthController::createWorkflowRequest(const QUrl& pUrl, const QVariant& pData, const AuthContext::BrowserHandler& pBrowserHandler) { const auto& handler = [](const QSharedPointer& pActiveWorkflow, const QSharedPointer& pWaitingWorkflow){ - if (QList{Action::AUTH, Action::SELF, Action::PIN}.contains(pActiveWorkflow->getAction())) + if (QList{Action::AUTH, Action::SELF_AUTH, Action::CHANGE_PIN}.contains(pActiveWorkflow->getAction())) { const auto activeContext = pActiveWorkflow->getContext(); if (activeContext->isWorkflowFinished() && pWaitingWorkflow.isNull()) diff -Nru ausweisapp2-2.3.1/src/workflows/base/paos/element/ElementParser.cpp ausweisapp2-2.4.0/src/workflows/base/paos/element/ElementParser.cpp --- ausweisapp2-2.3.1/src/workflows/base/paos/element/ElementParser.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/paos/element/ElementParser.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,8 +6,13 @@ #include "paos/element/ConnectionHandleParser.h" + using namespace governikus; + +Q_DECLARE_LOGGING_CATEGORY(paos) + + ElementParser::ElementParser(QSharedPointer pXmlReader) : mXmlReader(pXmlReader) , mParseError(false) @@ -127,3 +132,9 @@ mParseError |= parser.parserFailed(); return handle; } + + +const QLoggingCategory& ElementParser::getLoggingCategory() +{ + return paos(); +} diff -Nru ausweisapp2-2.3.1/src/workflows/base/paos/element/ElementParser.h ausweisapp2-2.4.0/src/workflows/base/paos/element/ElementParser.h --- ausweisapp2-2.3.1/src/workflows/base/paos/element/ElementParser.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/paos/element/ElementParser.h 2025-10-30 10:10:48.000000000 +0000 @@ -12,10 +12,9 @@ #include -Q_DECLARE_LOGGING_CATEGORY(paos) - class test_ElementParser; + namespace governikus { @@ -98,6 +97,8 @@ [[nodiscard]] QStringView getElementTypeByNamespace(const QString& pNamespace) const; private: + static const QLoggingCategory& getLoggingCategory(); + QSharedPointer mXmlReader; bool mParseError; }; @@ -107,7 +108,7 @@ { if (pList.isEmpty()) { - qCWarning(paos) << "Mandatory list is empty:" << pElementName; + qCWarning(getLoggingCategory()) << "Mandatory list is empty:" << pElementName; mParseError = true; return false; } diff -Nru ausweisapp2-2.3.1/src/workflows/base/paos/retrieve/DidAuthenticateEac1Parser.cpp ausweisapp2-2.4.0/src/workflows/base/paos/retrieve/DidAuthenticateEac1Parser.cpp --- ausweisapp2-2.3.1/src/workflows/base/paos/retrieve/DidAuthenticateEac1Parser.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/paos/retrieve/DidAuthenticateEac1Parser.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -206,8 +206,6 @@ { if (auto cvc = CVCertificate::fromRaw(QByteArray::fromHex(readElementText().toLatin1()))) { - qCDebug(paos) << "Linked Certificate (Authority):" << cvc->getBody().getCertificationAuthorityReference(); - qCDebug(paos) << "Certificate Name (Holder):" << cvc->getBody().getCertificateHolderReference(); pEac1.appendCvcerts(cvc); } else diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/AbstractState.cpp ausweisapp2-2.4.0/src/workflows/base/states/AbstractState.cpp --- ausweisapp2-2.3.1/src/workflows/base/states/AbstractState.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/AbstractState.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -174,7 +174,7 @@ } -void AbstractState::updateStatus(const GlobalStatus& pStatus) +void AbstractState::updateStatus(const GlobalStatus& pStatus) const { if (pStatus.isError() && mContext->getStatus().isNoError()) { diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/AbstractState.h ausweisapp2-2.4.0/src/workflows/base/states/AbstractState.h --- ausweisapp2-2.3.1/src/workflows/base/states/AbstractState.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/AbstractState.h 2025-10-30 10:10:48.000000000 +0000 @@ -66,7 +66,7 @@ void clearConnections(); bool isCancellationByUser() const; - void updateStatus(const GlobalStatus& pStatus); + void updateStatus(const GlobalStatus& pStatus) const; void updateStartPaosResult(const ECardApiResult& pStartPaosResult); void stopNfcScanIfNecessary(const QString& pError = QString()) const; diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StateCertificateDescriptionCheck.cpp ausweisapp2-2.4.0/src/workflows/base/states/StateCertificateDescriptionCheck.cpp --- ausweisapp2-2.3.1/src/workflows/base/states/StateCertificateDescriptionCheck.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StateCertificateDescriptionCheck.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -65,12 +65,12 @@ // check same origin policy for TCToken URL and subject URL const QString& subjectUrlString = getContext()->getDidAuthenticateEac1()->getCertificateDescription()->getSubjectUrl(); - const QUrl& tcTockenUrl = getContext()->getTcTokenUrl(); + const QUrl& tcTokenUrl = getContext()->getTcTokenUrl(); qDebug() << "Subject URL from AT CVC (eService certificate) description:" << subjectUrlString; - qDebug() << "TCToken URL:" << tcTockenUrl; + qDebug() << "TCToken URL:" << tcTokenUrl; - if (UrlUtil::isMatchingSameOriginPolicy(QUrl(subjectUrlString), tcTockenUrl)) + if (UrlUtil::isMatchingSameOriginPolicy(QUrl(subjectUrlString), tcTokenUrl)) { qDebug() << "SOP-Check succeeded."; } diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StateChangePin.cpp ausweisapp2-2.4.0/src/workflows/base/states/StateChangePin.cpp --- ausweisapp2-2.3.1/src/workflows/base/states/StateChangePin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StateChangePin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -64,8 +64,11 @@ } else { - //: INFO ALL_PLATFORMS The ID card PIN was changed successfully. - context->setSuccessMessage(tr("You have successfully changed your ID card PIN.")); + context->setSuccessMessage( + //: INFO ALL_PLATFORMS The ID card PIN was changed successfully. + tr("You have successfully changed your ID card PIN.") + QStringLiteral("
") + //: LABEL DESKTOP + + tr("You may now remove your ID card from the device.")); } Q_EMIT fireContinue(); return; diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StateCheckRefreshAddress.cpp ausweisapp2-2.4.0/src/workflows/base/states/StateCheckRefreshAddress.cpp --- ausweisapp2-2.3.1/src/workflows/base/states/StateCheckRefreshAddress.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StateCheckRefreshAddress.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -304,7 +304,7 @@ return; } - const QUrl& redirectUrl = mReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + const QUrl& redirectUrl = UrlUtil::resolveRedirect(mReply); if (redirectUrl.isEmpty()) { qCritical() << "Got empty redirect URL"; diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StateExtractCvcsFromEac1InputType.cpp ausweisapp2-2.4.0/src/workflows/base/states/StateExtractCvcsFromEac1InputType.cpp --- ausweisapp2-2.3.1/src/workflows/base/states/StateExtractCvcsFromEac1InputType.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StateExtractCvcsFromEac1InputType.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,10 +4,10 @@ #include "StateExtractCvcsFromEac1InputType.h" -#include "AppSettings.h" using namespace governikus; + StateExtractCvcsFromEac1InputType::StateExtractCvcsFromEac1InputType(const QSharedPointer& pContext) : AbstractState(pContext) , GenericContextContainer(pContext) diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StateGenericSendReceive.cpp ausweisapp2-2.4.0/src/workflows/base/states/StateGenericSendReceive.cpp --- ausweisapp2-2.3.1/src/workflows/base/states/StateGenericSendReceive.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StateGenericSendReceive.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -8,13 +8,16 @@ #include "CertificateChecker.h" #include "Env.h" #include "LogHandler.h" +#include "NetworkManager.h" #include "TlsChecker.h" #include "paos/PaosHandler.h" + Q_DECLARE_LOGGING_CATEGORY(secure) Q_DECLARE_LOGGING_CATEGORY(developermode) Q_DECLARE_LOGGING_CATEGORY(network) + using namespace governikus; @@ -133,29 +136,9 @@ TlsChecker::logSslConfig(cfg, spawnMessageLogger(network)); auto failure = checkSslConnectionAndSaveCertificate(cfg); - - auto context = getContext(); - if (!failure.has_value() && !context->getTcToken()->usePsk()) + if (!failure.has_value()) { - const auto& session = context->getSslSession(); - if (session.isEmpty() || session != cfg.sessionTicket()) - { - const auto& sessionFailedError = "Session resumption failed"; - - if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) - { - qCCritical(developermode) << sessionFailedError; - } - else - { - qCCritical(network) << sessionFailedError; - updateStatus({GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error, {GlobalStatus::ExternalInformation::LAST_URL, mReply->url().toString()} - }); - failure = {FailureCode::Reason::Generic_Send_Receive_Session_Resumption_Failed, - {FailureCode::Info::State_Name, getStateName()} - }; - } - } + failure = checkAndSaveSessionResumption(cfg); } if (failure.has_value()) @@ -167,6 +150,43 @@ } +std::optional StateGenericSendReceive::checkAndSaveSessionResumption(const QSslConfiguration& pSslConfiguration) const +{ + auto context = getContext(); + if (context->getTcToken()->usePsk()) + { + if (context->getSslSessionPsk() != pSslConfiguration.sessionTicket()) + { + qCDebug(network) << "Storing new session ticket"; + context->setSslSessionPsk(pSslConfiguration.sessionTicket()); + } + return {}; + } + + const auto& session = context->getSslSession(); + if (session.isEmpty() || session != pSslConfiguration.sessionTicket()) + { + const auto& sessionFailedError = "Session resumption failed"; + if (Env::getSingleton()->getGeneralSettings().isDeveloperMode()) + { + qCCritical(developermode) << sessionFailedError; + } + else + { + qCCritical(network) << sessionFailedError; + updateStatus({GlobalStatus::Code::Workflow_TrustedChannel_Establishment_Error, + {GlobalStatus::ExternalInformation::LAST_URL, mReply->url().toString()} + }); + + return FailureCode(FailureCode::Reason::Generic_Send_Receive_Session_Resumption_Failed, + {FailureCode::Info::State_Name, getStateName()}); + } + } + + return {}; +} + + void StateGenericSendReceive::onPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator* pAuthenticator) const { qCDebug(network) << "pre-shared key authentication requested:" << pAuthenticator->identityHint(); @@ -192,7 +212,7 @@ } -std::optional StateGenericSendReceive::checkSslConnectionAndSaveCertificate(const QSslConfiguration& pSslConfiguration) +std::optional StateGenericSendReceive::checkSslConnectionAndSaveCertificate(const QSslConfiguration& pSslConfiguration) const { const QSharedPointer context = getContext(); Q_ASSERT(!context.isNull()); @@ -271,7 +291,7 @@ qCDebug(network).noquote() << "Try to send raw data:\n" << data; const QByteArray& paosNamespace = PaosCreator::getNamespace(PaosCreator::Namespace::PAOS).toUtf8(); - const auto& session = token->usePsk() ? QByteArray() : getContext()->getSslSession(); + const auto& session = token->usePsk() ? getContext()->getSslSessionPsk() : getContext()->getSslSession(); mReply = Env::getSingleton()->paos(request, paosNamespace, data, token->usePsk(), session); *this << connect(mReply.data(), &QNetworkReply::sslErrors, this, &StateGenericSendReceive::onSslErrors); *this << connect(mReply.data(), &QNetworkReply::encrypted, this, &StateGenericSendReceive::onSslHandshakeDone); diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StateGenericSendReceive.h ausweisapp2-2.4.0/src/workflows/base/states/StateGenericSendReceive.h --- ausweisapp2-2.3.1/src/workflows/base/states/StateGenericSendReceive.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StateGenericSendReceive.h 2025-10-30 10:10:48.000000000 +0000 @@ -36,9 +36,10 @@ void logRawData(const QByteArray& pMessage); void setReceivedMessage(const QSharedPointer& pMessage) const; - std::optional checkSslConnectionAndSaveCertificate(const QSslConfiguration& pSslConfiguration); + std::optional checkSslConnectionAndSaveCertificate(const QSslConfiguration& pSslConfiguration) const; void onSslErrors(const QList& pErrors); void onSslHandshakeDone(); + std::optional checkAndSaveSessionResumption(const QSslConfiguration& pSslConfiguration) const; void run() override; protected: diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StateGetTcToken.cpp ausweisapp2-2.4.0/src/workflows/base/states/StateGetTcToken.cpp --- ausweisapp2-2.3.1/src/workflows/base/states/StateGetTcToken.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StateGetTcToken.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -49,7 +49,7 @@ } -bool StateGetTcToken::isValidRedirectUrl(const QUrl& pUrl) +bool StateGetTcToken::isValidRedirectUrl(const QUrl& pUrl) const { if (pUrl.isEmpty()) { @@ -159,10 +159,39 @@ if (mReply->error() != QNetworkReply::NoError) { - qCCritical(network) << NetworkManager::toStatus(mReply); - updateStatus(NetworkManager::toTrustedChannelStatus(mReply)); - Q_EMIT fireAbort({FailureCode::Reason::Get_TcToken_Network_Error, - {FailureCode::Info::Network_Error, mReply->errorString()} + GlobalStatus::Code errorStatus = GlobalStatus::Code::No_Error; + FailureCode::Reason reason; + if (statusCode >= 500) + { + if (statusCode == 503) + { + errorStatus = GlobalStatus::Code::Workflow_TrustedChannel_ServiceUnavailable; + reason = FailureCode::Reason::Get_TcToken_ServiceUnavailable; + } + else + { + errorStatus = GlobalStatus::Code::Workflow_TrustedChannel_Server_Error; + reason = FailureCode::Reason::Get_TcToken_Server_Error; + } + } + else if (statusCode >= 400) + { + errorStatus = GlobalStatus::Code::Workflow_TrustedChannel_Client_Error; + reason = FailureCode::Reason::Get_TcToken_Client_Error; + } + else + { + errorStatus = GlobalStatus::Code::Workflow_TrustedChannel_Other_Network_Error; + reason = FailureCode::Reason::Get_TcToken_Network_Error; + } + + const GlobalStatus::ExternalInfoMap infoMap { + {GlobalStatus::ExternalInformation::HTTP_STATUS_CODE, QString::number(statusCode)}, + {GlobalStatus::ExternalInformation::LAST_URL, mReply->url().toString()} + }; + updateStatus({errorStatus, infoMap}); + Q_EMIT fireAbort({reason, + {FailureCode::Info::Http_Status_Code, QString::number(statusCode)} }); return; } @@ -176,7 +205,7 @@ if (statusCode == HTTP_STATUS_SEE_OTHER || statusCode == HTTP_STATUS_FOUND || statusCode == HTTP_STATUS_TEMPORARY_REDIRECT) { - const QUrl& redirectUrl = mReply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + const QUrl& redirectUrl = UrlUtil::resolveRedirect(mReply); if (!isValidRedirectUrl(redirectUrl)) { Q_EMIT fireAbort(FailureCode::Reason::Get_TcToken_Invalid_Redirect_Url); @@ -187,40 +216,10 @@ } qCritical() << "Error while connecting to the provider. The server returns an unexpected status code:" << statusCode; - - GlobalStatus::Code errorStatus = GlobalStatus::Code::No_Error; - FailureCode::Reason reason; - if (statusCode >= 500) - { - if (statusCode == 503) - { - errorStatus = GlobalStatus::Code::Workflow_TrustedChannel_ServiceUnavailable; - reason = FailureCode::Reason::Get_TcToken_ServiceUnavailable; - } - else - { - errorStatus = GlobalStatus::Code::Workflow_TrustedChannel_Server_Error; - reason = FailureCode::Reason::Get_TcToken_Server_Error; - } - } - else if (statusCode >= 400) - { - errorStatus = GlobalStatus::Code::Workflow_TrustedChannel_Client_Error; - reason = FailureCode::Reason::Get_TcToken_Client_Error; - } - else - { - errorStatus = GlobalStatus::Code::Workflow_TrustedChannel_Server_Format_Error; - reason = FailureCode::Reason::Get_TcToken_Invalid_Server_Reply; - } - - const GlobalStatus::ExternalInfoMap infoMap { - {GlobalStatus::ExternalInformation::HTTP_STATUS_CODE, QString::number(statusCode)}, - {GlobalStatus::ExternalInformation::LAST_URL, mReply->url().toString()} - }; - updateStatus({errorStatus, infoMap}); - Q_EMIT fireAbort({reason, - {FailureCode::Info::Http_Status_Code, QString::number(statusCode)} + qCCritical(network) << NetworkManager::toStatus(mReply); + updateStatus(NetworkManager::toTrustedChannelStatus(mReply)); + Q_EMIT fireAbort({FailureCode::Reason::Get_TcToken_Invalid_Server_Reply, + {FailureCode::Info::Network_Error, mReply->errorString()} }); } diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StateGetTcToken.h ausweisapp2-2.4.0/src/workflows/base/states/StateGetTcToken.h --- ausweisapp2-2.3.1/src/workflows/base/states/StateGetTcToken.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StateGetTcToken.h 2025-10-30 10:10:48.000000000 +0000 @@ -29,7 +29,7 @@ void parseTcToken(); void sendRequest(const QUrl& pUrl); - bool isValidRedirectUrl(const QUrl& pUrl); + bool isValidRedirectUrl(const QUrl& pUrl) const; void run() override; explicit StateGetTcToken(const QSharedPointer& pContext); diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StatePreVerification.cpp ausweisapp2-2.4.0/src/workflows/base/states/StatePreVerification.cpp --- ausweisapp2-2.3.1/src/workflows/base/states/StatePreVerification.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StatePreVerification.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,6 @@ #include "StatePreVerification.h" #include "AppSettings.h" -#include "EnumHelper.h" #include "SecureStorage.h" #include "asn1/SignatureChecker.h" @@ -13,8 +12,10 @@ #include + Q_DECLARE_LOGGING_CATEGORY(developermode) + using namespace governikus; @@ -103,11 +104,9 @@ { qDebug() << "Check certificate chain validity on" << mValidationDateTime.toString(Qt::ISODate); - QListIterator> i(pCertificates); - i.toBack(); - while (i.hasPrevious()) + for (auto iter = pCertificates.crbegin(); iter != pCertificates.crend(); ++iter) { - auto cert = i.previous(); + auto cert = *iter; const QDate& expirationDate = cert->getBody().getCertificateExpirationDate(); const QDate& effectiveDate = cert->getBody().getCertificateEffectiveDate(); const QByteArray certInfo = cert->getBody().getCertificateHolderReference(); diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StateRedirectBrowser.cpp ausweisapp2-2.4.0/src/workflows/base/states/StateRedirectBrowser.cpp --- ausweisapp2-2.3.1/src/workflows/base/states/StateRedirectBrowser.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StateRedirectBrowser.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -36,8 +36,10 @@ } -void StateRedirectBrowser::run() +void StateRedirectBrowser::onEntry(QEvent* pEvent) { + AbstractState::onEntry(pEvent); + const auto& context = getContext(); if (const auto& url = context->getRefreshUrl(); url.isValid()) { @@ -71,6 +73,12 @@ qDebug() << "TcToken is missing"; } } +} + + +void StateRedirectBrowser::run() +{ + const auto& context = getContext(); if (const auto& handler = context->getBrowserHandler(); handler) { diff -Nru ausweisapp2-2.3.1/src/workflows/base/states/StateRedirectBrowser.h ausweisapp2-2.4.0/src/workflows/base/states/StateRedirectBrowser.h --- ausweisapp2-2.3.1/src/workflows/base/states/StateRedirectBrowser.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/base/states/StateRedirectBrowser.h 2025-10-30 10:10:48.000000000 +0000 @@ -26,6 +26,7 @@ private: explicit StateRedirectBrowser(const QSharedPointer& pContext); + void onEntry(QEvent* pEvent) override; void run() override; static QUrl addMajorMinor(const QUrl& pUrl, const ECardApiResult& pResult); diff -Nru ausweisapp2-2.3.1/src/workflows/ifd/states/StateEnterNewPacePinIfd.cpp ausweisapp2-2.4.0/src/workflows/ifd/states/StateEnterNewPacePinIfd.cpp --- ausweisapp2-2.3.1/src/workflows/ifd/states/StateEnterNewPacePinIfd.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/ifd/states/StateEnterNewPacePinIfd.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,6 +4,8 @@ #include "StateEnterNewPacePinIfd.h" +#include "ReaderManager.h" + using namespace governikus; @@ -32,6 +34,16 @@ } +void StateEnterNewPacePinIfd::onCardRemoved(const ReaderInfo& pInfo) const +{ + if (const auto& context = getContext(); pInfo.getName() == context->getReaderName()) + { + qDebug() << "Card was removed while waiting for user input. Resetting card connection"; + context->resetCardConnection(); + } +} + + void StateEnterNewPacePinIfd::onEntry(QEvent* pEvent) { AbstractState::onEntry(pEvent); @@ -47,4 +59,7 @@ } *this << connect(getContext().data(), &IfdServiceContext::fireUserError, this, &StateEnterNewPacePinIfd::onUserError); + + const auto* readerManager = Env::getSingleton(); + *this << connect(readerManager, &ReaderManager::fireCardRemoved, this, &StateEnterNewPacePinIfd::onCardRemoved); } diff -Nru ausweisapp2-2.3.1/src/workflows/ifd/states/StateEnterNewPacePinIfd.h ausweisapp2-2.4.0/src/workflows/ifd/states/StateEnterNewPacePinIfd.h --- ausweisapp2-2.3.1/src/workflows/ifd/states/StateEnterNewPacePinIfd.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/ifd/states/StateEnterNewPacePinIfd.h 2025-10-30 10:10:48.000000000 +0000 @@ -24,6 +24,7 @@ private Q_SLOTS: void onUserError(StatusCode pStatusCode); + void onCardRemoved(const ReaderInfo& pInfo) const; public: void onEntry(QEvent* pEvent) override; diff -Nru ausweisapp2-2.3.1/src/workflows/ifd/states/StateEnterPacePasswordIfd.cpp ausweisapp2-2.4.0/src/workflows/ifd/states/StateEnterPacePasswordIfd.cpp --- ausweisapp2-2.3.1/src/workflows/ifd/states/StateEnterPacePasswordIfd.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/ifd/states/StateEnterPacePasswordIfd.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,6 +4,8 @@ #include "StateEnterPacePasswordIfd.h" +#include "ReaderManager.h" + using namespace governikus; @@ -35,6 +37,16 @@ } +void StateEnterPacePasswordIfd::onCardRemoved(const ReaderInfo& pInfo) const +{ + if (const auto& context = getContext(); pInfo.getName() == context->getReaderName()) + { + qDebug() << "Card was removed while waiting for user input. Resetting card connection"; + context->resetCardConnection(); + } +} + + void StateEnterPacePasswordIfd::onEntry(QEvent* pEvent) { AbstractState::onEntry(pEvent); @@ -48,4 +60,7 @@ } *this << connect(getContext().data(), &IfdServiceContext::fireUserError, this, &StateEnterPacePasswordIfd::onUserError); + + const auto* readerManager = Env::getSingleton(); + *this << connect(readerManager, &ReaderManager::fireCardRemoved, this, &StateEnterPacePasswordIfd::onCardRemoved); } diff -Nru ausweisapp2-2.3.1/src/workflows/ifd/states/StateEnterPacePasswordIfd.h ausweisapp2-2.4.0/src/workflows/ifd/states/StateEnterPacePasswordIfd.h --- ausweisapp2-2.3.1/src/workflows/ifd/states/StateEnterPacePasswordIfd.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/ifd/states/StateEnterPacePasswordIfd.h 2025-10-30 10:10:48.000000000 +0000 @@ -29,6 +29,7 @@ private Q_SLOTS: void onUserError(); + void onCardRemoved(const ReaderInfo& pInfo) const; public: void onEntry(QEvent* pEvent) override; diff -Nru ausweisapp2-2.3.1/src/workflows/selfauth/SelfAuthenticationData.cpp ausweisapp2-2.4.0/src/workflows/selfauth/SelfAuthenticationData.cpp --- ausweisapp2-2.3.1/src/workflows/selfauth/SelfAuthenticationData.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/selfauth/SelfAuthenticationData.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -218,7 +218,7 @@ return; } - pSelfData << qMakePair(pGroupName, pGroupValue); + pSelfData << std::make_pair(pGroupName, pGroupValue); } @@ -248,15 +248,15 @@ QString SelfAuthenticationData::SelfData::formatDate(const QString& pDate) { - static const QList> formattingPattern({ + static const QList formattingPattern({ //: LABEL ALL_PLATFORMS Date format according to https://doc.qt.io/qt/qdate.html#toString - qMakePair(QStringLiteral("yyyy-MM-dd+hh:mm"), QLatin1String(QT_TR_NOOP("dd.MM.yyyy"))), + std::make_pair(QStringLiteral("yyyy-MM-dd+hh:mm"), QLatin1String(QT_TR_NOOP("dd.MM.yyyy"))), //: LABEL ALL_PLATFORMS Date format according to https://doc.qt.io/qt/qdate.html#toString with unknown day - qMakePair(QStringLiteral("yyyy-MM"), QLatin1String(QT_TR_NOOP("xx.MM.yyyy"))), + std::make_pair(QStringLiteral("yyyy-MM"), QLatin1String(QT_TR_NOOP("xx.MM.yyyy"))), //: LABEL ALL_PLATFORMS Additional date format with unknown day - qMakePair(QStringLiteral("yyyyMM"), QLatin1String(QT_TR_NOOP("xx.MM.yyyy"))), + std::make_pair(QStringLiteral("yyyyMM"), QLatin1String(QT_TR_NOOP("xx.MM.yyyy"))), //: LABEL ALL_PLATFORMS Date format according to https://doc.qt.io/qt/qdate.html#toString with unknown day and month - qMakePair(QStringLiteral("yyyy"), QLatin1String(QT_TR_NOOP("xx.xx.yyyy"))), + std::make_pair(QStringLiteral("yyyy"), QLatin1String(QT_TR_NOOP("xx.xx.yyyy"))), }); for (const auto& [key, value] : formattingPattern) @@ -319,7 +319,7 @@ } //: LABEL ALL_PLATFORMS - add(orderedSelfData, tr("Date of expiry"), formatDate(getValue(SelfAuthData::DateOfExpiry))); + add(orderedSelfData, tr("Valid until"), formatDate(getValue(SelfAuthData::DateOfExpiry))); return orderedSelfData; } diff -Nru ausweisapp2-2.3.1/src/workflows/selfauth/SelfAuthenticationData.h ausweisapp2-2.4.0/src/workflows/selfauth/SelfAuthenticationData.h --- ausweisapp2-2.3.1/src/workflows/selfauth/SelfAuthenticationData.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/selfauth/SelfAuthenticationData.h 2025-10-30 10:10:48.000000000 +0000 @@ -6,18 +6,19 @@ #include "EnumHelper.h" -#include - #include #include #include #include #include #include -#include #include #include +#include +#include + + namespace governikus { @@ -56,7 +57,7 @@ class SelfAuthenticationData { public: - using OrderedSelfData = QList>; + using OrderedSelfData = QList>; private: class SelfData diff -Nru ausweisapp2-2.3.1/src/workflows/selfauth/context/SelfAuthContext.cpp ausweisapp2-2.4.0/src/workflows/selfauth/context/SelfAuthContext.cpp --- ausweisapp2-2.3.1/src/workflows/selfauth/context/SelfAuthContext.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/src/workflows/selfauth/context/SelfAuthContext.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -7,7 +7,7 @@ using namespace governikus; SelfAuthContext::SelfAuthContext(bool pActivateUi) - : AuthContext(Action::SELF, pActivateUi) + : AuthContext(Action::SELF_AUTH, pActivateUi) , mSelfAuthenticationData() { } diff -Nru ausweisapp2-2.3.1/test/CMakeLists.txt ausweisapp2-2.4.0/test/CMakeLists.txt --- ausweisapp2-2.3.1/test/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -10,7 +10,17 @@ function(GET_TEST_CMDLINE cmdline testname) - set(${cmdline} ${PLATFORM} -v2 -o ${CMAKE_CURRENT_BINARY_DIR}/results.${testname}.log.xml,xml -o -,txt PARENT_SCOPE) + if(DEFINED ENV{GITLAB_CI} AND (LIBS_QT_PATCHES + OR QT_VERSION VERSION_GREATER_EQUAL "6.9.1" + OR (QT_VERSION VERSION_GREATER_EQUAL "6.8.4" AND QT_VERSION VERSION_LESS "6.9"))) + set(log_format junit.xml,junitxml) + elseif(DEFINED ENV{JENKINS_HOME}) + set(log_format log.xml,xml) + else() + set(log_format txt,txt) + endif() + + set(${cmdline} ${PLATFORM} -v2 -o -,txt -o ${CMAKE_CURRENT_BINARY_DIR}/results.${testname}.${log_format} PARENT_SCOPE) endfunction() function(GET_TEST_ENV testenv testname) @@ -48,9 +58,6 @@ endif() GET_TEST_CMDLINE(CMD_PARAMS ${TESTNAME}) - if(QT_VERSION VERSION_LESS "6.5") - set(CMD_PARAMS ${CMD_PARAMS} -import "qrc:/qt/qml") - endif() set(CMD_PARAMS ${CMD_PARAMS} -import "qrc:/qml") set(CMD $ ${CMD_PARAMS} -input ${sourcefile}) diff -Nru ausweisapp2-2.3.1/test/fixture/card/simulatorFiles.json ausweisapp2-2.4.0/test/fixture/card/simulatorFiles.json --- ausweisapp2-2.3.1/test/fixture/card/simulatorFiles.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/fixture/card/simulatorFiles.json 2025-10-30 10:10:48.000000000 +0000 @@ -28,8 +28,8 @@ ], "keys": [ - {"id": 1, "private": "0353859c2ec67780ba39015de8c682af2326d43de9ce1e07737087bd1e17cb22"}, - {"id": 2, "private": "9ad0ad7f4dfaaa06988339fc31d3a111f4c7964ac7f377373a2454327c43e2ff"}, - {"id": 41, "private": "a07eb62e891daa84643e0afcc1af006891b669b8f51e379477dbeab8c987a610"} + {"id": 1, "content": "308202050201003081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d0101022100a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377304404207d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9042026dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b60441048bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997022100a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a70201010482010f3082010b02010104200353859c2ec67780ba39015de8c682af2326d43de9ce1e07737087bd1e17cb22a081e33081e0020101302c06072a8648ce3d0101022100a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377304404207d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9042026dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b60441048bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997022100a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7020101"}, + {"id": 2, "content": "308202050201003081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d0101022100a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377304404207d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9042026dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b60441048bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997022100a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a70201010482010f3082010b02010104209ad0ad7f4dfaaa06988339fc31d3a111f4c7964ac7f377373a2454327c43e2ffa081e33081e0020101302c06072a8648ce3d0101022100a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377304404207d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9042026dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b60441048bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997022100a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7020101"}, + {"id": 41, "content": "308202050201003081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d0101022100a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377304404207d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9042026dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b60441048bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997022100a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a70201010482010f3082010b0201010420a07eb62e891daa84643e0afcc1af006891b669b8f51e379477dbeab8c987a610a081e33081e0020101302c06072a8648ce3d0101022100a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e5377304404207d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9042026dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b60441048bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f046997022100a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a7020101"} ] } diff -Nru ausweisapp2-2.3.1/test/fixture/core/diagnosis/firstRuleDisabled.txt ausweisapp2-2.4.0/test/fixture/core/diagnosis/firstRuleDisabled.txt --- ausweisapp2-2.3.1/test/fixture/core/diagnosis/firstRuleDisabled.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/fixture/core/diagnosis/firstRuleDisabled.txt 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -Name : AusweisApp-Firewall-Rule +Name : {DEA54BD7-0422-4C23-8499-C442CA99CD29} DisplayName : AusweisApp Description : AusweisApp firewall rule DisplayGroup : @@ -16,4 +16,4 @@ Status : Die Regel wurde erfolgreich vom Speicher aus analysiert. (65536) EnforcementStatus : NotApplicable PolicyStoreSource : PersistentStore -PolicyStoreSourceType : Local \ No newline at end of file +PolicyStoreSourceType : Local diff -Nru ausweisapp2-2.3.1/test/fixture/core/diagnosis/first_Rule.txt ausweisapp2-2.4.0/test/fixture/core/diagnosis/first_Rule.txt --- ausweisapp2-2.3.1/test/fixture/core/diagnosis/first_Rule.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/fixture/core/diagnosis/first_Rule.txt 2025-10-30 10:10:48.000000000 +0000 @@ -1,5 +1,5 @@ -Name : AusweisApp-Firewall-Rule +Name : {DEA54BD7-0422-4C23-8499-C442CA99CD29} DisplayName : AusweisApp Description : AusweisApp firewall rule DisplayGroup : diff -Nru ausweisapp2-2.3.1/test/fixture/updatable-files/supported-providers_no-prs.json ausweisapp2-2.4.0/test/fixture/updatable-files/supported-providers_no-prs.json --- ausweisapp2-2.3.1/test/fixture/updatable-files/supported-providers_no-prs.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/fixture/updatable-files/supported-providers_no-prs.json 2025-10-30 10:10:48.000000000 +0000 @@ -26,7 +26,7 @@ "address": "https://www.pin-ruecksetzbrief-bestellen.de", "homepage": "https://www.pin-ruecksetzbrief-bestellen.de/support", "phone": "+49 30 18 681 23333", - "postalAddress": "Bundesministerium des Innern und für Heimat
Alt-Moabit 140
10557 Berlin", + "postalAddress": "Bundesministerium des Innern
Alt-Moabit 140
10557 Berlin", "category": "citizen" } ] diff -Nru ausweisapp2-2.3.1/test/fixture/updatable-files/supported-providers_prs.json ausweisapp2-2.4.0/test/fixture/updatable-files/supported-providers_prs.json --- ausweisapp2-2.3.1/test/fixture/updatable-files/supported-providers_prs.json 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/fixture/updatable-files/supported-providers_prs.json 2025-10-30 10:10:48.000000000 +0000 @@ -27,7 +27,7 @@ "address": "https://www.pin-ruecksetzbrief-bestellen.de", "homepage": "https://www.pin-ruecksetzbrief-bestellen.de/support", "phone": "+49 30 18 681 23333", - "postalAddress": "Bundesministerium des Innern und für Heimat
Alt-Moabit 140
10557 Berlin", + "postalAddress": "Bundesministerium des Innern
Alt-Moabit 140
10557 Berlin", "category": "citizen" } ] diff -Nru ausweisapp2-2.3.1/test/helper/common/CMakeLists.txt ausweisapp2-2.4.0/test/helper/common/CMakeLists.txt --- ausweisapp2-2.3.1/test/helper/common/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/common/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -4,6 +4,10 @@ target_link_libraries(AusweisAppTestHelperCommon AusweisAppCardSimulator AusweisAppNetwork AusweisAppCore) target_link_libraries(AusweisAppTestHelperCommon AusweisAppWorkflowsSelfAuth) +if(TARGET ${Qt}::CorePrivate) + target_link_libraries(AusweisAppTestHelperCommon ${Qt}::CorePrivate) +endif() + target_compile_definitions(AusweisAppTestHelperCommon PRIVATE QT_STATICPLUGIN) if(DESKTOP) diff -Nru ausweisapp2-2.3.1/test/helper/common/MockCard.cpp ausweisapp2-2.4.0/test/helper/common/MockCard.cpp --- ausweisapp2-2.3.1/test/helper/common/MockCard.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/common/MockCard.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -39,11 +39,21 @@ { qFatal("No (more) response APDU configured, but a(nother) command transmitted"); } - QPair config = mCardConfig.mTransmits.takeFirst(); + std::pair config = mCardConfig.mTransmits.takeFirst(); return {config.first, ResponseApdu(config.second)}; } +EstablishPaceChannelOutput MockCard::establishPaceChannel(PacePasswordId pPasswordId, int pPreferredPinLength, const QByteArray& pChat, const QByteArray& pCertificateDescription) +{ + Q_UNUSED(pPasswordId) + Q_UNUSED(pPreferredPinLength) + Q_UNUSED(pChat) + Q_UNUSED(pCertificateDescription) + return EstablishPaceChannelOutput(CardReturnCode::INVALID_PASSWORD); +} + + void MockCard::setConnected(bool pConnected) { mConnected = pConnected; diff -Nru ausweisapp2-2.3.1/test/helper/common/MockCard.h ausweisapp2-2.4.0/test/helper/common/MockCard.h --- ausweisapp2-2.3.1/test/helper/common/MockCard.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/common/MockCard.h 2025-10-30 10:10:48.000000000 +0000 @@ -8,12 +8,14 @@ #include #include -#include + +#include + namespace governikus { -using TransmitConfig = QPair; +using TransmitConfig = std::pair; class MockCardConfig @@ -55,6 +57,8 @@ ResponseApduResult transmit(const CommandApdu& pCmd) override; + EstablishPaceChannelOutput establishPaceChannel(PacePasswordId pPasswordId, int pPreferredPinLength, const QByteArray& pChat, const QByteArray& pCertificateDescription) override; + void setConnected(bool pConnected); }; diff -Nru ausweisapp2-2.3.1/test/helper/common/MockNetworkReply.h ausweisapp2-2.4.0/test/helper/common/MockNetworkReply.h --- ausweisapp2-2.3.1/test/helper/common/MockNetworkReply.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/common/MockNetworkReply.h 2025-10-30 10:10:48.000000000 +0000 @@ -19,6 +19,7 @@ Q_OBJECT private: + QSslConfiguration mSslConfig; MockSocket mSocket; public: @@ -56,6 +57,24 @@ } + void setSslConfigurationImplementation(const QSslConfiguration& pConfiguration) override + { + mSslConfig = pConfiguration; + } + + + void sslConfigurationImplementation(QSslConfiguration& configuration) const override + { + configuration = mSslConfig; + } + + + QUrl url() const + { + return QUrl(); + } + + [[nodiscard]] qint64 bytesAvailable() const override; qint64 readData(char* pDst, qint64 pMaxSize) override; diff -Nru ausweisapp2-2.3.1/test/helper/common/MockReaderManagerPlugin.cpp ausweisapp2-2.4.0/test/helper/common/MockReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/test/helper/common/MockReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/common/MockReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -2,11 +2,11 @@ * Copyright (c) 2015-2025 Governikus GmbH & Co. KG, Germany */ - #include "MockReaderManagerPlugin.h" #include + using namespace governikus; @@ -30,22 +30,16 @@ } -QList MockReaderManagerPlugin::getReaders() const +QPointer MockReaderManagerPlugin::getReader(const QString& pReaderName) const { - QList readers; - readers.reserve(mReaders.size()); - for (MockReader* reader : mReaders) - { - readers += reader; - } - return readers; + return mReaders.value(pReaderName).data(); } void MockReaderManagerPlugin::startScan(bool pAutoConnect) { ReaderManagerPlugin::startScan(pAutoConnect); - for (MockReader* reader : std::as_const(mReaders)) + for (const auto& reader : std::as_const(mReaders)) { const auto& readerInfo = reader->getReaderInfo(); if (readerInfo.isInsertable()) @@ -59,18 +53,17 @@ } -MockReader* MockReaderManagerPlugin::addReader(const QString& pReaderName, ReaderManagerPluginType pType) +QPointer MockReaderManagerPlugin::addReader(const QString& pReaderName, ReaderManagerPluginType pType) { - MockReader* mockReader = nullptr; + QSharedPointer mockReader; QMetaObject::invokeMethod(this, [this, pReaderName, pType] { - auto* reader = new MockReader(pReaderName, pType); - reader->setParent(this); + const auto& reader = QSharedPointer::create(pReaderName, pType); - connect(reader, &Reader::fireCardInserted, this, &ReaderManagerPlugin::fireCardInserted); - connect(reader, &Reader::fireCardRemoved, this, &ReaderManagerPlugin::fireCardRemoved); - connect(reader, &Reader::fireCardInfoChanged, this, &ReaderManagerPlugin::fireCardInfoChanged); - connect(reader, &Reader::fireReaderPropertiesUpdated, this, &ReaderManagerPlugin::fireReaderPropertiesUpdated); + connect(reader.data(), &Reader::fireCardInserted, this, &ReaderManagerPlugin::fireCardInserted); + connect(reader.data(), &Reader::fireCardRemoved, this, &ReaderManagerPlugin::fireCardRemoved); + connect(reader.data(), &Reader::fireCardInfoChanged, this, &ReaderManagerPlugin::fireCardInfoChanged); + connect(reader.data(), &Reader::fireReaderPropertiesUpdated, this, &ReaderManagerPlugin::fireReaderPropertiesUpdated); mReaders.insert(pReaderName, reader); Q_EMIT fireReaderAdded(reader->getReaderInfo()); @@ -79,17 +72,16 @@ }, Qt::BlockingQueuedConnection, &mockReader); QCoreApplication::processEvents(); - return mockReader; + return mockReader.data(); } void MockReaderManagerPlugin::removeReader(const QString& pReaderName) { QMetaObject::invokeMethod(this, [this, pReaderName] { - if (auto reader = mReaders.take(pReaderName)) + if (const auto& reader = mReaders.take(pReaderName)) { Q_EMIT fireReaderRemoved(reader->getReaderInfo()); - delete reader; } }, Qt::BlockingQueuedConnection); QCoreApplication::processEvents(); @@ -98,13 +90,13 @@ void MockReaderManagerPlugin::removeAllReader() { - QList readerList; + QList> readerList; QMetaObject::invokeMethod(this, [this] { - return getReaders(); + return mReaders.values(); }, Qt::BlockingQueuedConnection, &readerList); - for (auto reader : std::as_const(readerList)) + for (const auto& reader : std::as_const(readerList)) { removeReader(reader->getName()); } @@ -114,7 +106,7 @@ void MockReaderManagerPlugin::insert(const QString& pReaderName, const QVariant& pData) { - for (MockReader* reader : std::as_const(mReaders)) + for (const auto& reader : std::as_const(mReaders)) { if (reader->getName() == pReaderName) { @@ -122,3 +114,12 @@ } } } + + +void MockReaderManagerPlugin::shelveAll() const +{ + for (const auto& reader : mReaders) + { + shelve(reader.data()); + } +} diff -Nru ausweisapp2-2.3.1/test/helper/common/MockReaderManagerPlugin.h ausweisapp2-2.4.0/test/helper/common/MockReaderManagerPlugin.h --- ausweisapp2-2.3.1/test/helper/common/MockReaderManagerPlugin.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/common/MockReaderManagerPlugin.h 2025-10-30 10:10:48.000000000 +0000 @@ -8,6 +8,7 @@ #include "ReaderManagerPlugin.h" #include +#include #include @@ -23,7 +24,7 @@ private: static MockReaderManagerPlugin* mInstance; - QMap mReaders; + QMap> mReaders; public: MockReaderManagerPlugin(); @@ -32,12 +33,14 @@ static MockReaderManagerPlugin& getInstance(); void insert(const QString& pReaderName, const QVariant& pData) override; - [[nodiscard]] QList getReaders() const override; + [[nodiscard]] QPointer getReader(const QString& pReaderName) const override; void startScan(bool pAutoConnect) override; - MockReader* addReader(const QString& pReaderName = QStringLiteral("MockReader"), ReaderManagerPluginType pType = MockReader::cMOCKED_READERMANAGER_TYPE); + QPointer addReader(const QString& pReaderName = QStringLiteral("MockReader"), ReaderManagerPluginType pType = MockReader::cMOCKED_READERMANAGER_TYPE); void removeReader(const QString& pReaderName); void removeAllReader(); + void shelveAll() const override; + using ReaderManagerPlugin::setPluginAvailable; }; diff -Nru ausweisapp2-2.3.1/test/helper/ifd/MockIfdDispatcher.cpp ausweisapp2-2.4.0/test/helper/ifd/MockIfdDispatcher.cpp --- ausweisapp2-2.3.1/test/helper/ifd/MockIfdDispatcher.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/ifd/MockIfdDispatcher.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -8,6 +8,10 @@ #include "messages/IfdConnectResponse.h" #include "messages/IfdDisconnect.h" #include "messages/IfdDisconnectResponse.h" +#include "messages/IfdEstablishPaceChannel.h" +#include "messages/IfdEstablishPaceChannelResponse.h" +#include "messages/IfdModifyPin.h" +#include "messages/IfdModifyPinResponse.h" #include "messages/IfdStatus.h" #include "messages/IfdTransmit.h" #include "messages/IfdTransmitResponse.h" @@ -82,6 +86,35 @@ return; } + if (mState == DispatcherState::ReaderWithCardRemoved) + { + Q_EMIT fireReceived(IfdMessageType::IFDStatus, + QJsonDocument::fromJson("{\n" + " \"CardAvailable\": false,\n" + " \"ConnectedReader\": true,\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"EFATR\": null,\n" + " \"EFDIR\": null,\n" + " \"MaxAPDULength\": 500,\n" + " \"PINPad\": false,\n" + " \"SlotName\": \"SlotName\",\n" + " \"msg\": \"IFDStatus\"\n" + "}\n").object(), mId); + + Q_EMIT fireReceived(IfdMessageType::IFDStatus, + QJsonDocument::fromJson("{\n" + " \"CardAvailable\": true,\n" + " \"ConnectedReader\": true,\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"EFATR\": null,\n" + " \"EFDIR\": null,\n" + " \"MaxAPDULength\": 500,\n" + " \"PINPad\": false,\n" + " \"SlotName\": \"SlotName\",\n" + " \"msg\": \"IFDStatus\"\n" + "}\n").object(), mId); + } + if (pMessage->getType() == IfdMessageType::IFDEstablishContext) { bool withCard = (mState == DispatcherState::ReaderWithCard || mState == DispatcherState::ReaderWithCardError); @@ -91,7 +124,20 @@ return; } - const ECardApiResult::Minor resultMinor = (mState == DispatcherState::ReaderWithCardError ? ECardApiResult::Minor::AL_Unknown_Error : ECardApiResult::Minor::null); + ECardApiResult::Minor resultMinor = ECardApiResult::Minor::null; + switch (mState) + { + case DispatcherState::ReaderWithCardError: + resultMinor = ECardApiResult::Minor::AL_Unknown_Error; + break; + + case DispatcherState::ReaderWithCardRemoved: + resultMinor = ECardApiResult::Minor::IFDL_Terminal_NoCard; + break; + + default: + break; + } if (pMessage->getType() == IfdMessageType::IFDConnect) { @@ -116,6 +162,22 @@ const QSharedPointer message(new IfdDisconnectResponse(readerName, resultMinor)); Q_EMIT fireReceived(message->getType(), QJsonDocument::fromJson(message->toByteArray(IfdVersion::Version::v2, mContextHandle)).object(), mId); } + + if (pMessage->getType() == IfdMessageType::IFDEstablishPACEChannel) + { + const QSharedPointer request = pMessage.staticCast(); + const QString readerName = request->getSlotHandle(); + const QSharedPointer message(new IfdEstablishPaceChannelResponse(readerName, EstablishPaceChannelOutput(CardReturnCode::OK), resultMinor)); + Q_EMIT fireReceived(message->getType(), QJsonDocument::fromJson(message->toByteArray(IfdVersion::Version::v2, mContextHandle)).object(), mId); + } + + if (pMessage->getType() == IfdMessageType::IFDModifyPIN) + { + const QSharedPointer request = pMessage.staticCast(); + const QString readerName = request->getSlotHandle(); + const QSharedPointer message(new IfdModifyPinResponse(readerName, QByteArray(), resultMinor)); + Q_EMIT fireReceived(message->getType(), QJsonDocument::fromJson(message->toByteArray(IfdVersion::Version::v2, mContextHandle)).object(), mId); + } } diff -Nru ausweisapp2-2.3.1/test/helper/ifd/MockIfdDispatcher.h ausweisapp2-2.4.0/test/helper/ifd/MockIfdDispatcher.h --- ausweisapp2-2.3.1/test/helper/ifd/MockIfdDispatcher.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/ifd/MockIfdDispatcher.h 2025-10-30 10:10:48.000000000 +0000 @@ -22,7 +22,8 @@ ReaderInvalid, ReaderWithoutCard, ReaderWithCard, - ReaderWithCardError + ReaderWithCardError, + ReaderWithCardRemoved }; private: diff -Nru ausweisapp2-2.3.1/test/helper/ui/json/MsgHandlerEnterPassword.cpp ausweisapp2-2.4.0/test/helper/ui/json/MsgHandlerEnterPassword.cpp --- ausweisapp2-2.3.1/test/helper/ui/json/MsgHandlerEnterPassword.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/ui/json/MsgHandlerEnterPassword.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,13 +6,14 @@ #include "MockCardConnection.h" #include "MockReaderManagerPlugin.h" -#include "ReaderManager.h" #include + using namespace Qt::Literals::StringLiterals; using namespace governikus; + void governikus::setValidState(MessageDispatcher& pDispatcher, bool pSelectReader, bool pBasicReader, @@ -46,7 +47,7 @@ if (pSelectReader) { - auto* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader CARD"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader CARD"_L1); auto info = reader->getReaderInfo(); info.setBasicReader(pBasicReader); reader->setReaderInfo(info); diff -Nru ausweisapp2-2.3.1/test/helper/ui/websocket/WebSocketHelper.cpp ausweisapp2-2.4.0/test/helper/ui/websocket/WebSocketHelper.cpp --- ausweisapp2-2.3.1/test/helper/ui/websocket/WebSocketHelper.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/ui/websocket/WebSocketHelper.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -25,7 +25,7 @@ WebSocketHelper::WebSocketHelper(int pPort, int pConnectionTimeout) - : mConnectionTiemout(pConnectionTimeout) + : mConnectionTimeout(pConnectionTimeout) { connect(&mWebSocket, &QWebSocket::textMessageReceived, this, &WebSocketHelper::onTextMessageReceived); connectWebsocket(pPort); @@ -64,11 +64,11 @@ QEventLoop eventLoop; connect(&mWebSocket, &QWebSocket::textMessageReceived, &eventLoop, &QEventLoop::quit); - QTimer::singleShot(mConnectionTiemout, &eventLoop, &QEventLoop::quit); + QTimer::singleShot(mConnectionTimeout, &eventLoop, &QEventLoop::quit); eventLoop.exec(); } - while (QDateTime::currentMSecsSinceEpoch() - start < mConnectionTiemout); + while (QDateTime::currentMSecsSinceEpoch() - start < mConnectionTimeout); return false; } diff -Nru ausweisapp2-2.3.1/test/helper/ui/websocket/WebSocketHelper.h ausweisapp2-2.4.0/test/helper/ui/websocket/WebSocketHelper.h --- ausweisapp2-2.3.1/test/helper/ui/websocket/WebSocketHelper.h 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/helper/ui/websocket/WebSocketHelper.h 2025-10-30 10:10:48.000000000 +0000 @@ -18,7 +18,7 @@ Q_OBJECT private: - const int mConnectionTiemout; + const int mConnectionTimeout; QWebSocket mWebSocket; QStringList mInput; diff -Nru ausweisapp2-2.3.1/test/integrated/CMakeLists.txt ausweisapp2-2.4.0/test/integrated/CMakeLists.txt --- ausweisapp2-2.3.1/test/integrated/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/integrated/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -7,7 +7,7 @@ set_tests_properties(${TESTNAME} PROPERTIES LABELS "integrated" TIMEOUT 60) set_tests_properties(${TESTNAME} PROPERTIES FAIL_REGULAR_EXPRESSION "WARNING: QApplication was not created in the main") -if(NOT QT_VENDOR STREQUAL "Governikus") +if(NOT LIBS_GOVERNIKUS) list(APPEND ENV "ASAN_OPTIONS=detect_leaks=0") set_tests_properties(${TESTNAME} PROPERTIES ENVIRONMENT "${ENV}") endif() diff -Nru ausweisapp2-2.3.1/test/integrated/test_Integrated.cpp ausweisapp2-2.4.0/test/integrated/test_Integrated.cpp --- ausweisapp2-2.3.1/test/integrated/test_Integrated.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/integrated/test_Integrated.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -65,9 +65,13 @@ int main() { - governikus::QtHooks::init(); + if (!governikus::QtHooks::init()) + { + std::cout << "Cannot initialize QtHooks" << std::endl; + return -2; + } -#if defined(GOVERNIKUS_QT) +#if defined(LIBS_GOVERNIKUS) start_ausweisapp(nullptr); #else start_ausweisapp("--no-proxy"); @@ -80,7 +84,7 @@ governikus::QtHooks::printAlive(); std::cout << "There are zombies behind you: " << livingDeadCount << std::endl; -#ifdef GOVERNIKUS_QT +#ifdef LIBS_GOVERNIKUS return livingDeadCount; #endif diff -Nru ausweisapp2-2.3.1/test/integration/init/test_CommandLineParser.cpp ausweisapp2-2.4.0/test/integration/init/test_CommandLineParser.cpp --- ausweisapp2-2.3.1/test/integration/init/test_CommandLineParser.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/integration/init/test_CommandLineParser.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -41,11 +41,7 @@ out << QString(); out << QStringLiteral("Options:"); out << QStringLiteral(" -h, --help Displays help on commandline options."); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) out << QStringLiteral(" --help-all Displays help, including generic Qt options."); -#else - out << QStringLiteral(" --help-all Displays help including Qt specific options."); -#endif out << QStringLiteral(" -v, --version Displays version information."); out << QStringLiteral(" --keep Keep logfile."); out << QStringLiteral(" --no-logfile Disable logfile."); diff -Nru ausweisapp2-2.3.1/test/integration/ui/qml/test_Qml.cpp ausweisapp2-2.4.0/test/integration/ui/qml/test_Qml.cpp --- ausweisapp2-2.3.1/test/integration/ui/qml/test_Qml.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/integration/ui/qml/test_Qml.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -91,8 +91,10 @@ { QTest::addColumn("platform"); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)) QTest::newRow("Android") << u"android"_s; QTest::newRow("iOS") << u"ios"_s; +#endif QTest::newRow("Desktop") << u""_s; } diff -Nru ausweisapp2-2.3.1/test/qml/+mobile/test_ProgressView.qml ausweisapp2-2.4.0/test/qml/+mobile/test_ProgressView.qml --- ausweisapp2-2.3.1/test/qml/+mobile/test_ProgressView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/+mobile/test_ProgressView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -9,7 +9,7 @@ function test_load_ProgressView() { let item = createTemporaryQmlObject(" import Governikus.ProgressView - ProgressView { title: \"ProgessView\" } + ProgressView { title: \"ProgressView\" } ", parent); item.destroy(); } diff -Nru ausweisapp2-2.3.1/test/qml/AuthView/+desktop/test_AuthView.qml ausweisapp2-2.4.0/test/qml/AuthView/+desktop/test_AuthView.qml --- ausweisapp2-2.3.1/test/qml/AuthView/+desktop/test_AuthView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/AuthView/+desktop/test_AuthView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -24,9 +24,6 @@ ", testCase); } function test_load() { - if (hasBindingLoop && Style.is_layout_desktop) { - skip("Skip test because of QTBUG-110899"); - } let testObject = createTestObject(); verify(testObject, "Object loaded"); } diff -Nru ausweisapp2-2.3.1/test/qml/AuthView/+mobile/test_AuthView.qml ausweisapp2-2.4.0/test/qml/AuthView/+mobile/test_AuthView.qml --- ausweisapp2-2.3.1/test/qml/AuthView/+mobile/test_AuthView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/AuthView/+mobile/test_AuthView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -24,9 +24,6 @@ ", testCase); } function test_load() { - if (hasBindingLoop && Style.is_layout_desktop) { - skip("Skip test because of QTBUG-110899"); - } let testObject = createTestObject(); verify(testObject, "Object loaded"); } diff -Nru ausweisapp2-2.3.1/test/qml/AuthView/test_AuthModel.qml ausweisapp2-2.4.0/test/qml/AuthView/test_AuthModel.qml --- ausweisapp2-2.3.1/test/qml/AuthView/test_AuthModel.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/AuthView/test_AuthModel.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ +import QtQuick +import QtTest + +TestCase { + id: testCase + + function createTestObject() { + return createTemporaryQmlObject(" + import QtQuick + import Governikus.Type + + Item { + readonly property int testStatus: GlobalStatusCode.No_Error + readonly property int modelStatus: AuthModel.statusCode + readonly property string testStatusString: \"Error code: Unknown_Error\" + readonly property string modelStatusString: AuthModel.statusCodeDisplayString + } + ", testCase); + } + function test_load() { + let testObject = createTestObject(); + verify(testObject, "Object loaded"); + verify(testObject.testStatus, testObject.modelStatus); + verify(testObject.testStatusString, testObject.statusCodeDisplayString); + } + + name: "test_AuthModelQml" + visible: true + when: windowShown +} diff -Nru ausweisapp2-2.3.1/test/qml/CMakeLists.txt ausweisapp2-2.4.0/test/qml/CMakeLists.txt --- ausweisapp2-2.3.1/test/qml/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -8,9 +8,6 @@ endif() target_link_libraries(QmlTestRunner ${Qt}::QuickTest ${Qt}::Gui AusweisAppGlobal AusweisAppUiQml AusweisAppTestHelper) -if(WIN32) - ADD_SHADERS_TO_TARGET(QmlTestRunner) -endif() if(CMAKE_GENERATOR MATCHES "Xcode") set_target_properties(QmlTestRunner PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${CMAKE_BUILD_TYPE} "${CMAKE_CURRENT_BINARY_DIR}") diff -Nru ausweisapp2-2.3.1/test/qml/FeedbackView/+mobile/test_ListItem.qml ausweisapp2-2.4.0/test/qml/FeedbackView/+mobile/test_ListItem.qml --- ausweisapp2-2.3.1/test/qml/FeedbackView/+mobile/test_ListItem.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/FeedbackView/+mobile/test_ListItem.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -/** - * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick -import QtTest - -TestCase { - id: testCase - - function createTestObject() { - return createTemporaryQmlObject(" - import Governikus.FeedbackView - ListItem { - width: 200 - height: 50 - } - ", testCase); - } - function test_icon() { - let testObject = createTestObject(); - compare(testObject.icon, "", "Initial icon: empty"); - testObject.icon = "qrc:///images/material_check.svg"; - compare(testObject.icon, "qrc:///images/material_check.svg", "icon: qrc:///images/material_check.svg"); - verify(!testObject.tintIcon, "Initial tintIcon: false"); - testObject.tintIcon = true; - verify(testObject.tintIcon, "tintIcon: true"); - } - function test_load() { - let testObject = createTestObject(); - verify(testObject, "Object loaded"); - } - function test_showSeparator() { - let testObject = createTestObject(); - verify(testObject.showSeparator, "Initial showSeparator: true"); - testObject.showSeparator = false; - verify(!testObject.showSeparator, "showSeparator: false"); - } - function test_text() { - let testObject = createTestObject(); - compare(testObject.text, "", "Initial text: empty"); - testObject.text = "test"; - compare(testObject.text, "test", "text: test"); - compare(testObject.headerText, "", "Initial headerText: empty"); - testObject.headerText = "test"; - compare(testObject.headerText, "test", "headerText: test"); - compare(testObject.footerText, "", "Initial footerText: empty"); - testObject.footerText = "test"; - compare(testObject.footerText, "test", "footerText: test"); - } - - name: "test_ListItem" - visible: true - when: windowShown -} diff -Nru ausweisapp2-2.3.1/test/qml/FeedbackView/+mobile/test_LogFilesView.qml ausweisapp2-2.4.0/test/qml/FeedbackView/+mobile/test_LogFilesView.qml --- ausweisapp2-2.3.1/test/qml/FeedbackView/+mobile/test_LogFilesView.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/FeedbackView/+mobile/test_LogFilesView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +import QtTest +import QtQuick + +import Governikus.FeedbackView + +Item { + height: 400 + width: 400 + + LogFilesView { + id: logfilesView + + anchors.fill: parent + } + SignalSpy { + id: signalSpy + + signalName: "logFilesListItemClicked" + target: logfilesView + } + TestCase { + id: testCase + + function test_load() { + let testObject = createTemporaryQmlObject("import Governikus.FeedbackView; LogFilesView {}", testCase); + verify(testObject, "Object loaded"); + } + function test_whenItemClicked_signalIsEmitted() { + testCase.Window.window.requestActivate(); + tryCompare(testCase.Window.window, "active", true); + compare(signalSpy.count, 0); + mouseClick(logfilesView, 20, 30); + compare(signalSpy.count, 1); + } + + name: "LogFilesViewTest" + when: windowShown + } +} diff -Nru ausweisapp2-2.3.1/test/qml/FeedbackView/+mobile/test_LogView.qml ausweisapp2-2.4.0/test/qml/FeedbackView/+mobile/test_LogView.qml --- ausweisapp2-2.3.1/test/qml/FeedbackView/+mobile/test_LogView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/FeedbackView/+mobile/test_LogView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,10 +1,9 @@ /** * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany */ + import QtQuick import QtTest -import Governikus.Global -import Governikus.Style TestCase { id: testCase diff -Nru ausweisapp2-2.3.1/test/qml/Global/+desktop/test_GFileDialog.qml ausweisapp2-2.4.0/test/qml/Global/+desktop/test_GFileDialog.qml --- ausweisapp2-2.3.1/test/qml/Global/+desktop/test_GFileDialog.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/Global/+desktop/test_GFileDialog.qml 2025-10-30 10:10:48.000000000 +0000 @@ -1,6 +1,7 @@ /** * Copyright (c) 2021-2025 Governikus GmbH & Co. KG, Germany */ + import QtQuick import QtTest @@ -14,6 +15,13 @@ let testObject = createTestObject(); verify(testObject, "Object loaded"); } + function test_selectFile() { + let testObject = createTestObject(); + compare(testObject.selectedFile.toString(), ""); + testObject.selectFile("test"); + let endsWithTest = /test$/; + verify(endsWithTest.test(testObject.selectedFile)); + } name: "test_GFileDialog" visible: true diff -Nru ausweisapp2-2.3.1/test/qml/Global/+desktop/test_LocationButton.qml ausweisapp2-2.4.0/test/qml/Global/+desktop/test_LocationButton.qml --- ausweisapp2-2.3.1/test/qml/Global/+desktop/test_LocationButton.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/Global/+desktop/test_LocationButton.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick -import QtTest -import Governikus.Global -import Governikus.Type - -TestCase { - id: testCase - - function createTestObject() { - return createTemporaryQmlObject("import Governikus.Global; LocationButton { - a11yDescription: \"a11yDescription\" - a11yName: \"a11yName\" - image: \"qrc:///images/npa.svg\" - language: \"language\" - languageText: \"languageText\" - }", testCase); - } - function test_load() { - let testObject = createTestObject(); - verify(testObject, "Object loaded"); - } - - name: "test_LocationButton" - visible: true - when: windowShown -} diff -Nru ausweisapp2-2.3.1/test/qml/Global/+desktop/test_ProxyCredentialsPopup.qml ausweisapp2-2.4.0/test/qml/Global/+desktop/test_ProxyCredentialsPopup.qml --- ausweisapp2-2.3.1/test/qml/Global/+desktop/test_ProxyCredentialsPopup.qml 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/Global/+desktop/test_ProxyCredentialsPopup.qml 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany + */ +import QtQuick +import QtTest +import QtQuick.Controls +import Governikus.Global +import Governikus.Type + +TestCase { + id: testCase + + function createTestObject() { + return createTemporaryQmlObject("import Governikus.Global; ProxyCredentialsPopup {}", testCase); + } + function test_load() { + let testObject = createTestObject(); + verify(testObject, "Object loaded"); + } + + name: "test_ProxyCredentialsPopup" + visible: true + when: windowShown + + MockProxyCredentials { + id: credentialContainer + + } + ProxyCredentialsPopup { + id: testObject + + property string lastEvent + + width: 1000 + + onCancelled: lastEvent = "cancelled" + onConfirmed: lastEvent = "confirmed" + + TestCase { + function test_checkContainerValues() { + testObject.open(); + testObject.accept(); + compare(testObject.lastEvent, "confirmed"); + testObject.open(); + testObject.cancel(); + compare(testObject.lastEvent, "cancelled"); + testObject.credentials = null; + credentialContainer.user = "TestUser"; + credentialContainer.password = "TestPassword"; + credentialContainer.confirmed = false; + testObject.credentials = credentialContainer; + testObject.open(); + testObject.accept(); + compare(credentialContainer.user, "ProposedUser"); + compare(credentialContainer.password, ""); + verify(credentialContainer.confirmed); + compare(testObject.lastEvent, "confirmed"); + testObject.credentials = null; + credentialContainer.user = "TestUser"; + credentialContainer.password = "TestPassword"; + credentialContainer.confirmed = false; + testObject.credentials = credentialContainer; + testObject.open(); + testObject.cancel(); + compare(credentialContainer.user, "TestUser"); + compare(credentialContainer.password, "TestPassword"); + verify(credentialContainer.confirmed); + compare(testObject.lastEvent, "cancelled"); + } + + when: windowShown + } + } + + component MockProxyCredentials: ProxyCredentials { + property bool confirmed + property string password + readonly property string proposedUser: "ProposedUser" + property string user + + function confirmInput() { + confirmed = true; + } + } +} diff -Nru ausweisapp2-2.3.1/test/qml/Global/+desktop/test_TabbedPane.qml ausweisapp2-2.4.0/test/qml/Global/+desktop/test_TabbedPane.qml --- ausweisapp2-2.3.1/test/qml/Global/+desktop/test_TabbedPane.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/Global/+desktop/test_TabbedPane.qml 2025-10-30 10:10:48.000000000 +0000 @@ -58,10 +58,8 @@ function test_setIndex() { testObject.currentIndex = 2; compare(testObject.currentIndex, 2, "Set index 2"); - if (canUseTypeCast) { - verify(testObject.currentItemModel !== null, "The current model (index=2) should not be null"); - compare(testObject.currentItemModel.modelData, "Item 2", "Current modelData should be: Item 2"); - } + verify(testObject.currentItemModel !== null, "The current model (index=2) should not be null"); + compare(testObject.currentItemModel.modelData, "Item 2", "Current modelData should be: Item 2"); } when: windowShown diff -Nru ausweisapp2-2.3.1/test/qml/Global/+mobile/test_GCollapsibleSubButton.qml ausweisapp2-2.4.0/test/qml/Global/+mobile/test_GCollapsibleSubButton.qml --- ausweisapp2-2.3.1/test/qml/Global/+mobile/test_GCollapsibleSubButton.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/Global/+mobile/test_GCollapsibleSubButton.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -/** - * Copyright (c) 2023-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick -import QtTest -import Governikus.Global - -TestCase { - id: testCase - - function createTestObject() { - return createTemporaryQmlObject("import Governikus.Global; GCollapsibleSubButton { - image: \"qrc:///images/material_check.svg\" - }", testCase); - } - function test_load() { - let testObject = createTestObject(); - verify(testObject, "Object loaded"); - compare(testObject.image, "qrc:///images/material_check.svg", "Image: qrc:///images/material_check.svg"); - } - function test_text() { - let testObject = createTestObject(); - compare(testObject.text, "", "Initial title: empty"); - testObject.text = "test"; - compare(testObject.text, "test", "text: test"); - } - - name: "test_GCollapsibleSubButton" - visible: true - when: windowShown -} diff -Nru ausweisapp2-2.3.1/test/qml/Global/test_GComboBox.qml ausweisapp2-2.4.0/test/qml/Global/test_GComboBox.qml --- ausweisapp2-2.3.1/test/qml/Global/test_GComboBox.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/Global/test_GComboBox.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick -import QtTest -import Governikus.Global -import Governikus.Style - -TestCase { - id: testCase - - function createTestObject() { - return createTemporaryQmlObject("import Governikus.Global; GComboBox {}", testCase); - } - function test_initial() { - let testObject = createTestObject(); - compare(testObject.count, 0, "count: -1"); - compare(testObject.currentIndex, -1, "currentIndex: -1"); - verify(testObject.indicator.visible, "Indicator visible has to be: true"); - } - function test_load() { - let testObject = createTestObject(); - verify(testObject, "Object loaded"); - } - function test_textStyle() { - let testObject = createTestObject(); - compare(testObject.textStyle, Style.text.normal, "Initial textStyle: normal"); - } - - name: "test_GComboBox" - visible: true - when: windowShown - - GComboBox { - id: testObject - - model: ["a", "b", "c", "d"] - - TestCase { - function test_click() { - verify(!testObject.popup.visible, "Initial popup visible: false"); - mouseClick(testObject); - tryVerify(function () { - return testObject.popup; - }); - compare(testObject.popup.visible, true); - mouseClick(testObject); - tryVerify(function () { - return testObject.popup; - }); - compare(testObject.popup.visible, false); - } - function test_model() { - compare(testObject.count, 4, "count: 4"); - verify(testObject.indicator.visible, "Indicator visible: true"); - compare(testObject.currentIndex, 0, "currentIndex: 0"); - compare(testObject.currentText, "a", "currentText: a"); - testObject.currentIndex = 1; - compare(testObject.currentIndex, 1, "currentIndex: 1"); - compare(testObject.currentText, "b", "currentText: b"); - } - - when: windowShown - } - } -} diff -Nru ausweisapp2-2.3.1/test/qml/Global/test_GFlickable.qml ausweisapp2-2.4.0/test/qml/Global/test_GFlickable.qml --- ausweisapp2-2.3.1/test/qml/Global/test_GFlickable.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/Global/test_GFlickable.qml 2025-10-30 10:10:48.000000000 +0000 @@ -20,13 +20,13 @@ let childrenCount = testFlickable.columnChildren.length; for (let i = 0; i < childrenCount; ++i) { let child = testFlickable.columnChildren[i]; - testFlickable.positionViewAtItem(child); + Utils.positionViewAtItem(child); verify(testFlickable.contentY >= child.y); verify(testFlickable.contentY <= (child.y + child.height)); } for (let j = childrenCount - 1; j >= 0; --j) { let child = testFlickable.columnChildren[j]; - testFlickable.positionViewAtItem(child); + Utils.positionViewAtItem(child); verify(testFlickable.contentY >= child.y); verify(testFlickable.contentY <= (child.y + child.height)); } diff -Nru ausweisapp2-2.3.1/test/qml/Global/test_GRadioButton.qml ausweisapp2-2.4.0/test/qml/Global/test_GRadioButton.qml --- ausweisapp2-2.3.1/test/qml/Global/test_GRadioButton.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/Global/test_GRadioButton.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -/** - * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick -import QtTest -import Governikus.Global -import Governikus.Style - -TestCase { - id: testCase - - function createTestObject() { - return createTemporaryQmlObject("import Governikus.Global; GRadioButton {}", testCase); - } - function test_icon() { - let testObject = createTestObject(); - compare(testObject.icon.source, "", "Initial no image"); - testObject.icon.source = "qrc:///images/material_check.svg"; - compare(testObject.icon.source, "qrc:///images/material_check.svg", "Image: qrc:///images/material_check.svg"); - } - function test_load() { - let testObject = createTestObject(); - verify(testObject, "Object loaded"); - } - function test_text() { - let testObject = createTestObject(); - compare(testObject.text, "", "Initial empty text"); - testObject.text = "test"; - compare(testObject.text, "test", "text: test"); - } - function test_tintIcon() { - let testObject = createTestObject(); - verify(!testObject.tintIcon, "Initial tintIcon: false"); - testObject.tintIcon = true; - verify(testObject.tintIcon, "tintIcon: true"); - } - - name: "test_GRadioButton" - visible: true - when: windowShown - - Item { - GRadioButton { - id: button_a - - checked: true - } - GRadioButton { - id: button_b - - } - TestCase { - function test_selection() { - verify(button_a.checked, "button_a checked: true"); - verify(!button_b.checked, "button_b checked: false"); - mouseClick(button_b); - verify(!button_a.checked, "button_a checked: false"); - verify(button_b.checked, "button_b checked: true"); - } - - when: windowShown - } - } -} diff -Nru ausweisapp2-2.3.1/test/qml/Global/test_ProxyCredentialsPopup.qml ausweisapp2-2.4.0/test/qml/Global/test_ProxyCredentialsPopup.qml --- ausweisapp2-2.3.1/test/qml/Global/test_ProxyCredentialsPopup.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/Global/test_ProxyCredentialsPopup.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2020-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick -import QtTest -import QtQuick.Controls -import Governikus.Global -import Governikus.Type - -TestCase { - id: testCase - - function createTestObject() { - return createTemporaryQmlObject("import Governikus.Global; ProxyCredentialsPopup {}", testCase); - } - function test_load() { - let testObject = createTestObject(); - verify(testObject, "Object loaded"); - } - - name: "test_ProxyCredentialsPopup" - visible: true - when: windowShown - - MockProxyCredentials { - id: credentialContainer - - } - ProxyCredentialsPopup { - id: testObject - - property string lastEvent - - width: 1000 - - onCancelled: lastEvent = "cancelled" - onConfirmed: lastEvent = "confirmed" - - TestCase { - function test_checkContainerValues() { - testObject.open(); - testObject.accept(); - compare(testObject.lastEvent, "confirmed"); - testObject.open(); - testObject.cancel(); - compare(testObject.lastEvent, "cancelled"); - testObject.credentials = null; - credentialContainer.user = "TestUser"; - credentialContainer.password = "TestPassword"; - credentialContainer.confirmed = false; - testObject.credentials = credentialContainer; - testObject.open(); - testObject.accept(); - compare(credentialContainer.user, "ProposedUser"); - compare(credentialContainer.password, ""); - verify(credentialContainer.confirmed); - compare(testObject.lastEvent, "confirmed"); - testObject.credentials = null; - credentialContainer.user = "TestUser"; - credentialContainer.password = "TestPassword"; - credentialContainer.confirmed = false; - testObject.credentials = credentialContainer; - testObject.open(); - testObject.cancel(); - compare(credentialContainer.user, "TestUser"); - compare(credentialContainer.password, "TestPassword"); - verify(credentialContainer.confirmed); - compare(testObject.lastEvent, "cancelled"); - } - - when: windowShown - } - } - - component MockProxyCredentials: ProxyCredentials { - property bool confirmed - property string password - readonly property string proposedUser: "ProposedUser" - property string user - - function confirmInput() { - confirmed = true; - } - } -} diff -Nru ausweisapp2-2.3.1/test/qml/PasswordInfoView/test_MultiInfoView.qml ausweisapp2-2.4.0/test/qml/PasswordInfoView/test_MultiInfoView.qml --- ausweisapp2-2.3.1/test/qml/PasswordInfoView/test_MultiInfoView.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/PasswordInfoView/test_MultiInfoView.qml 2025-10-30 10:10:48.000000000 +0000 @@ -11,6 +11,7 @@ let item = createTemporaryQmlObject(" import Governikus.MultiInfoView MultiInfoView { + width: 500 infoContent: MultiInfoData { contentType: %1 } diff -Nru ausweisapp2-2.3.1/test/qml/QmlTestRunner.cpp ausweisapp2-2.4.0/test/qml/QmlTestRunner.cpp --- ausweisapp2-2.3.1/test/qml/QmlTestRunner.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/QmlTestRunner.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -43,7 +43,6 @@ Env::getSingleton()->init(); mMockNetworkManager.reset(new MockNetworkManager()); Env::set(NetworkManager::staticMetaObject, mMockNetworkManager.get()); - UiPluginQml::registerQmlTypes(); } @@ -60,23 +59,6 @@ { const auto& prefix = UiPluginQml::adjustQmlImportPath(pEngine); pEngine->rootContext()->setContextProperty(QStringLiteral("importPrefix"), prefix); -#if (QT_VERSION < QT_VERSION_CHECK(6, 4, 2)) - pEngine->rootContext()->setContextProperty(QStringLiteral("hasBindingLoop"), true); -#else - pEngine->rootContext()->setContextProperty(QStringLiteral("hasBindingLoop"), false); -#endif -#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 2)) - pEngine->rootContext()->setContextProperty(QStringLiteral("hasPolishLoop"), true); -#else - pEngine->rootContext()->setContextProperty(QStringLiteral("hasPolishLoop"), false); -#endif -#if (QT_VERSION < QT_VERSION_CHECK(6, 6, 0)) - pEngine->rootContext()->setContextProperty(QStringLiteral("canUseTypeCast"), false); -#else - // Even though some UTs, that use casts, would fail on 6.4 and 6.5, the app itself is not - // affected and works without any issues or concerning log outputs. - pEngine->rootContext()->setContextProperty(QStringLiteral("canUseTypeCast"), true); -#endif connect(pEngine, &QQmlEngine::warnings, this, [](const QList& pWarnings){ bool fail = false; diff -Nru ausweisapp2-2.3.1/test/qml/View/+mobile/test_ContentArea.qml ausweisapp2-2.4.0/test/qml/View/+mobile/test_ContentArea.qml --- ausweisapp2-2.3.1/test/qml/View/+mobile/test_ContentArea.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/View/+mobile/test_ContentArea.qml 2025-10-30 10:10:48.000000000 +0000 @@ -11,9 +11,6 @@ return createTemporaryQmlObject("import Governikus.View; ContentArea { width: 500 }", testCase); } function test_load() { - if (hasPolishLoop) { - skip("Skip test because of old Qt version with unfixed Polish Loops"); - } let testObject = createTestObject(); verify(testObject, "Object loaded"); } diff -Nru ausweisapp2-2.3.1/test/qml/View/test_FocusPoint.qml ausweisapp2-2.4.0/test/qml/View/test_FocusPoint.qml --- ausweisapp2-2.3.1/test/qml/View/test_FocusPoint.qml 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qml/View/test_FocusPoint.qml 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -/** - * Copyright (c) 2018-2025 Governikus GmbH & Co. KG, Germany - */ -import QtQuick -import QtTest - -TestCase { - id: testCase - - function createTestObject() { - return createTemporaryQmlObject("import Governikus.View; FocusPoint {}", testCase); - } - function test_load() { - let testObject = createTestObject(); - verify(testObject, "Object loaded"); - } - - name: "test_FocusPoint" - visible: true - when: windowShown -} diff -Nru ausweisapp2-2.3.1/test/qt/card/asn1/test_AccessRoleAndRight.cpp ausweisapp2-2.4.0/test/qt/card/asn1/test_AccessRoleAndRight.cpp --- ausweisapp2-2.3.1/test/qt/card/asn1/test_AccessRoleAndRight.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/asn1/test_AccessRoleAndRight.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -201,7 +201,7 @@ QTest::newRow("privilegedTerminal") << AccessRight::PRIVILEGED_TERMINAL << "Privileged terminal"_L1; QTest::newRow("pseudonymousSignatureAuthentication") << AccessRight::PSA << "Pseudonym"_L1; QTest::newRow("restrictedIdentification") << AccessRight::RESTRICTED_IDENTIFICATION << "Pseudonym"_L1; - QTest::newRow("comunityIdVerification") << AccessRight::COMMUNITY_ID_VERIFICATION << "Address verification"_L1; + QTest::newRow("communityIdVerification") << AccessRight::COMMUNITY_ID_VERIFICATION << "Address verification"_L1; QTest::newRow("ageVerification") << AccessRight::AGE_VERIFICATION << "Age verification"_L1; QTest::newRow("default") << static_cast(100) << "Unknown"_L1; diff -Nru ausweisapp2-2.3.1/test/qt/card/asn1/test_AuthenticatedAuxiliaryData.cpp ausweisapp2-2.4.0/test/qt/card/asn1/test_AuthenticatedAuxiliaryData.cpp --- ausweisapp2-2.3.1/test/qt/card/asn1/test_AuthenticatedAuxiliaryData.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/asn1/test_AuthenticatedAuxiliaryData.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -81,20 +81,20 @@ QTest::newRow("invalid") << QString() << QDate(); - QTest::newRow("vor der Geburt (1 Jahr, 2 Tage)") << QStringLiteral("-2") << QDate(1977, 8, 14); - QTest::newRow("vor der Geburt (2 Tage)") << QStringLiteral("-1") << QDate(1978, 8, 14); - QTest::newRow("vor der Geburt (1 Tag)") << QStringLiteral("-1") << QDate(1978, 8, 15); + QTest::newRow("vor der Geburt (1 Jahr, 2 Tage)") << QStringLiteral("-2") << QDate(1977, 8, 14); // codespell:ignore + QTest::newRow("vor der Geburt (2 Tage)") << QStringLiteral("-1") << QDate(1978, 8, 14); // codespell:ignore + QTest::newRow("vor der Geburt (1 Tag)") << QStringLiteral("-1") << QDate(1978, 8, 15); // codespell:ignore QTest::newRow("Geburt") << QStringLiteral("0") << QDate(1978, 8, 16); QTest::newRow("1 Tag") << QStringLiteral("0") << QDate(1978, 8, 17); - QTest::newRow("Ende des Jahres") << QStringLiteral("0") << QDate(1978, 12, 31); + QTest::newRow("Ende des Jahres") << QStringLiteral("0") << QDate(1978, 12, 31); // codespell:ignore QTest::newRow("Anfang naechstes Jahr") << QStringLiteral("0") << QDate(1979, 1, 1); QTest::newRow("vorm 1. Geburtstag") << QStringLiteral("0") << QDate(1979, 8, 15); QTest::newRow("1. Geburtstag") << QStringLiteral("1") << QDate(1979, 8, 16); QTest::newRow("nach 1. Geburtstag") << QStringLiteral("1") << QDate(1979, 8, 17); - QTest::newRow("Ende Monats nach 1. Geburtstag") << QStringLiteral("1") << QDate(1979, 8, 31); + QTest::newRow("Ende Monats nach 1. Geburtstag") << QStringLiteral("1") << QDate(1979, 8, 31); // codespell:ignore } diff -Nru ausweisapp2-2.3.1/test/qt/card/asn1/test_CVCertificate.cpp ausweisapp2-2.4.0/test/qt/card/asn1/test_CVCertificate.cpp --- ausweisapp2-2.3.1/test/qt/card/asn1/test_CVCertificate.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/asn1/test_CVCertificate.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -20,11 +20,17 @@ { Q_OBJECT + private: + QByteArray readFile(const QString& pFileName) + { + return TestFileHelper::readFile(QStringLiteral(":/card/").append(pFileName)); + } + private Q_SLOTS: void getRawBody() { QByteArray body("7f4e82016e5f290100420e44454356434165494430303130337f4982011d060a04007f000702020202038120a9fb57dba1eea9bc3e660a909d838d726e3bf623d52620282013481d1f6e537782207d5a0975fc2c3057eef67530417affe7fb8055c126dc5c6ce94a4b44f330b5d9832026dc5c6ce94a4b44f330b5d9bbd77cbf958416295cf7e1ce6bccdc18ff8c07b68441048bd2aeb9cb7e57cb2c4b482ffc81b7afb9de27e1e3bd23c23a4453bd9ace3262547ef835c3dac4fd97f8461a14611dc9c27745132ded8e545c1d54c72f0469978520a9fb57dba1eea9bc3e660a909d838d718c397aa3b561a6f7901e0e82974856a78641048925419fc7f194922cfc6b8dd25ae6a19c1b59216e6cf06270e5d75cfd64205f55cf867bbfefeefd6e680e1fd197f18ab684484901362568efc9adb5c6018d728701015f200e44454356434165494430303130337f4c12060904007f0007030102025305fc0f13ffff5f25060102010200035f2406010501020003"); - QByteArray source = TestFileHelper::readFile(":/card/cvca-DECVCAeID00103.hex"_L1); + QByteArray source = readFile("cvca-DECVCAeID00103.hex"_L1); auto cvc = Converter::certificatefromHex(source); QCOMPARE(cvc->getRawBody(), QByteArray::fromHex(body)); @@ -34,7 +40,7 @@ void getRawSignature() { QByteArray signature("5f37406018cff8ee22ae417a63446a5fbf53ac39dd1ee331af4b62569604966f7575359b0c536673dcb98f9ae62c5f9724e0236bef6dc485ffa331be5e82de4410e2e8"); - QByteArray source = TestFileHelper::readFile(":/card/cvca-DECVCAeID00103.hex"_L1); + QByteArray source = readFile("cvca-DECVCAeID00103.hex"_L1); auto cvc = Converter::certificatefromHex(source); QCOMPARE(cvc->getRawSignature(), QByteArray::fromHex(signature)); @@ -43,7 +49,7 @@ void toHex() { - QByteArray source = TestFileHelper::readFile(":/card/cvca-DECVCAeID00103.hex"_L1); + QByteArray source = readFile("cvca-DECVCAeID00103.hex"_L1); auto cvc = Converter::certificatefromHex(source); QCOMPARE(cvc->encode().toHex(), source); @@ -66,7 +72,7 @@ void testIsValidOn() { - QByteArray source = TestFileHelper::readFile(":/card/cvca-DETESTeID00001.hex"_L1); + QByteArray source = readFile("cvca-DETESTeID00001.hex"_L1); auto cvc = Converter::certificatefromHex(source); QVERIFY(!cvc->isValidOn(QDateTime(QDate(2010, 8, 12), QTime(22, 59, 59, 999), QTimeZone(-60 * 60 * 1)))); @@ -79,21 +85,22 @@ void testParseCrap() { + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Cannot decode ASN.1 object: "_L1)); QVERIFY(Converter::certificatefromHex(QByteArray("01020304060708")) == nullptr); } void testSuccess() { - QByteArray source = TestFileHelper::readFile(":/card/cvca-DETESTeID00001.hex"_L1); + QByteArray source = readFile("cvca-DETESTeID00001.hex"_L1); QVERIFY(Converter::certificatefromHex(source) != nullptr); } void testIsIssuedBy() { - auto cvca1 = Converter::certificatefromHex(TestFileHelper::readFile(":/card/cvca-DETESTeID00001.hex"_L1)); - auto cvcaLink2_1 = Converter::certificatefromHex(TestFileHelper::readFile(":/card/cvca-DETESTeID00002_DETESTeID00001.hex"_L1)); + auto cvca1 = Converter::certificatefromHex(readFile("cvca-DETESTeID00001.hex"_L1)); + auto cvcaLink2_1 = Converter::certificatefromHex(readFile("cvca-DETESTeID00002_DETESTeID00001.hex"_L1)); QVERIFY(cvca1->isIssuedBy(*cvca1)); QVERIFY(cvcaLink2_1->isIssuedBy(*cvca1)); @@ -104,7 +111,7 @@ void getSignature() { QByteArray signature("9f25ebfaf4b91e4c60a1683754c5dc076a3179753ef97d9f8cb01fe1dcd3b8c83e7a26602ab1f344be5706006d79a9ff6a9716404dc83b9f30e1213b393128a2"); - auto cvca1 = Converter::certificatefromHex(TestFileHelper::readFile(":/card/cvca-DETESTeID00001.hex"_L1)); + auto cvca1 = Converter::certificatefromHex(readFile("cvca-DETESTeID00001.hex"_L1)); QCOMPARE(cvca1->getSignature().toHex(), signature); } @@ -112,31 +119,31 @@ void debugStream() { - const auto& regex = QStringLiteral(R"(CVC(type=CVCA, car="DETESTeID00001", chr="DETESTeID00001", valid=["2010-08-13","2013-08-13"])"); - const QRegularExpression output(QRegularExpression::escape(regex)); - - QTest::ignoreMessage(QtDebugMsg, output); - QSharedPointer cvca = Converter::certificatefromHex(TestFileHelper::readFile(":/card/cvca-DETESTeID00001.hex"_L1)); + QTest::ignoreMessage(QtDebugMsg, "CVCA(DETESTeID00001, authority=DETESTeID00001, 2010-08-13 - 2013-08-13, invalid)"); + QSharedPointer cvca = Converter::certificatefromHex(readFile("cvca-DETESTeID00001.hex"_L1)); qDebug() << cvca; - QTest::ignoreMessage(QtDebugMsg, output); - QSharedPointer cvcsNonConst = qSharedPointerConstCast(cvca); - qDebug() << cvcsNonConst; + QTest::ignoreMessage(QtDebugMsg, "[(Self Signed) DETESTeID00001]"); + QList> cvcsVector({cvca}); + qDebug() << cvcsVector; - QTest::ignoreMessage(QtDebugMsg, output); - QList> cvcsVector = {qSharedPointerConstCast(cvca)}; + cvcsVector.append(Converter::certificatefromHex(readFile("cvca-DETESTeID00002_DETESTeID00001.hex"_L1))); + cvcsVector.append(Converter::certificatefromHex(readFile("cvca-DETESTeID00004_DETESTeID00002.hex"_L1))); + cvcsVector.append(Converter::certificatefromHex(readFile("cvdv-DEDVeIDDPST00035.hex"_L1))); + cvcsVector.append(Converter::certificatefromHex(readFile("cvat-DEDEMODEV00038.hex"_L1))); + QTest::ignoreMessage(QtDebugMsg, "[(Self Signed) DETESTeID00001, DETESTeID00002, DETESTeID00004, DEDVeIDDPST00035, DEDEMODEV00038]"); qDebug() << cvcsVector; - QTest::ignoreMessage(QtDebugMsg, output); - QList> cvcsVectorConst({cvca}); - qDebug() << cvcsVectorConst; + cvcsVector.pop_front(); + QTest::ignoreMessage(QtDebugMsg, "[DETESTeID00002, DETESTeID00004, DEDVeIDDPST00035, DEDEMODEV00038]"); + qDebug() << cvcsVector; } void equals() { - QSharedPointer cvca1_const = Converter::certificatefromHex(TestFileHelper::readFile(":/card/cvca-DETESTeID00001.hex"_L1)); - QSharedPointer cvca2_const = Converter::certificatefromHex(TestFileHelper::readFile(":/card/cvca-DETESTeID00001.hex"_L1)); + QSharedPointer cvca1_const = Converter::certificatefromHex(readFile("cvca-DETESTeID00001.hex"_L1)); + QSharedPointer cvca2_const = Converter::certificatefromHex(readFile("cvca-DETESTeID00001.hex"_L1)); QSharedPointer cvca1 = qSharedPointerConstCast(cvca1_const); QSharedPointer cvca2 = qSharedPointerConstCast(cvca2_const); @@ -151,8 +158,8 @@ void notEquals() { - QSharedPointer cvca1_const = Converter::certificatefromHex(TestFileHelper::readFile(":/card/cvca-DETESTeID00001.hex"_L1)); - QSharedPointer cvca2_const = Converter::certificatefromHex(TestFileHelper::readFile(":/card/cvca-DETESTeID00002.hex"_L1)); + QSharedPointer cvca1_const = Converter::certificatefromHex(readFile("cvca-DETESTeID00001.hex"_L1)); + QSharedPointer cvca2_const = Converter::certificatefromHex(readFile("cvca-DETESTeID00002.hex"_L1)); QSharedPointer cvca1 = qSharedPointerConstCast(cvca1_const); QSharedPointer cvca2 = qSharedPointerConstCast(cvca2_const); diff -Nru ausweisapp2-2.3.1/test/qt/card/asn1/test_CertificateDescription.cpp ausweisapp2-2.4.0/test/qt/card/asn1/test_CertificateDescription.cpp --- ausweisapp2-2.3.1/test/qt/card/asn1/test_CertificateDescription.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/asn1/test_CertificateDescription.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -191,7 +191,7 @@ QString termsOfUsage("

CertificateDescription mit HTML

\n" "

\n" - "Dies ist ein Test, zur Überprüfung einer CertificateDescription mit HTML-Elementen.\n" + "Dies ist ein Test, zur Überprüfung einer CertificateDescription mit HTML-Elementen.\n" // codespell:ignore "

\n" "

\n" "Klappt's?\n" diff -Nru ausweisapp2-2.3.1/test/qt/card/asn1/test_ChainBuilder.cpp ausweisapp2-2.4.0/test/qt/card/asn1/test_ChainBuilder.cpp --- ausweisapp2-2.3.1/test/qt/card/asn1/test_ChainBuilder.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/asn1/test_ChainBuilder.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -19,19 +19,18 @@ /* - * Der einfacheren Testbarkeit halber werden Ketten aus QStrings statt - * Zertifikatsketten gebildet. + * For easier testability, chains of QByteArray are created instead of + * certificate chains. * - * Die QStrings sind jeweils zwei Zeichen lang. Das erste Zeichen entspricht - * dem Issuer, das zweite Zeichen entspricht dem Subject. - * Die Verkettungsvorschrift ist durch die Methode test_ChainBuilder::isChild - * definiert. + * The QByteArray are each two characters long. The first character + * represents the issuer, and the second character represents the subject. + * The chaining rule is defined by the method test_ChainBuilder::isChild. */ static bool isChild(const QByteArray& pChild, const QByteArray& pParent) { if (pChild.at(1) == pChild.at(0)) { - // selbst signierte Zertifikate haben kein anderes Issuer-Zert + // Self-signed certificates do not have an external issuer certificate. return false; } return pChild.at(0) == pParent.at(1); @@ -124,7 +123,7 @@ } - void testManyLeafs() + void testManyLeaves() { /* * AA diff -Nru ausweisapp2-2.3.1/test/qt/card/asn1/test_Chat.cpp ausweisapp2-2.4.0/test/qt/card/asn1/test_Chat.cpp --- ausweisapp2-2.3.1/test/qt/card/asn1/test_Chat.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/asn1/test_Chat.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -173,7 +173,7 @@ } - void testUnsupprtedType() + void testUnsupportedType() { // see test case TS_TA_2.1.1 from TR-03105-5.2 QByteArray hexEncodedChat = QByteArray("7F4C12060904007F000703010201").append("5305FC0F13FFFF"); diff -Nru ausweisapp2-2.3.1/test/qt/card/asn1/test_Oid.cpp ausweisapp2-2.4.0/test/qt/card/asn1/test_Oid.cpp --- ausweisapp2-2.3.1/test/qt/card/asn1/test_Oid.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/asn1/test_Oid.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -60,7 +60,7 @@ } - void copyContructor_data() + void copyConstructor_data() { QTest::addColumn("oid"); QTest::addColumn("warnings"); @@ -71,7 +71,7 @@ } - void copyContructor() + void copyConstructor() { QFETCH(QByteArray, oid); QFETCH(bool, warnings); @@ -85,7 +85,7 @@ } - void moveContructor_data() + void moveConstructor_data() { QTest::addColumn("oid"); QTest::addColumn("warnings"); @@ -96,7 +96,7 @@ } - void moveContructor() + void moveConstructor() { QFETCH(QByteArray, oid); QFETCH(bool, warnings); diff -Nru ausweisapp2-2.3.1/test/qt/card/asn1/test_PaceInfo.cpp ausweisapp2-2.4.0/test/qt/card/asn1/test_PaceInfo.cpp --- ausweisapp2-2.3.1/test/qt/card/asn1/test_PaceInfo.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/asn1/test_PaceInfo.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -311,7 +311,7 @@ } - void paramterId_data() + void parameterId_data() { QTest::addColumn("parameterId"); QTest::addColumn("curveNid"); @@ -333,7 +333,7 @@ } - void paramterId() + void parameterId() { QFETCH(int, parameterId); QFETCH(int, curveNid); diff -Nru ausweisapp2-2.3.1/test/qt/card/asn1/test_SecurityInfos.cpp ausweisapp2-2.4.0/test/qt/card/asn1/test_SecurityInfos.cpp --- ausweisapp2-2.3.1/test/qt/card/asn1/test_SecurityInfos.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/asn1/test_SecurityInfos.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -135,7 +135,7 @@ } - void testAllPossibleMoibleEIDTypeInfo_data() + void testAllPossibleMobileEIDTypeInfo_data() { QTest::addColumn("hexString"); QTest::addColumn("expectedOid"); @@ -169,7 +169,7 @@ } - void testAllPossibleMoibleEIDTypeInfo() + void testAllPossibleMobileEIDTypeInfo() { QFETCH(QByteArray, hexString); QFETCH(Oid, expectedOid); diff -Nru ausweisapp2-2.3.1/test/qt/card/asn1/test_SignatureChecker.cpp ausweisapp2-2.4.0/test/qt/card/asn1/test_SignatureChecker.cpp --- ausweisapp2-2.3.1/test/qt/card/asn1/test_SignatureChecker.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/asn1/test_SignatureChecker.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -60,6 +60,7 @@ QList> certs; SignatureChecker checker(certs); + QTest::ignoreMessage(QtCriticalMsg, "Certificate chain is empty"); QVERIFY(!checker.check()); } @@ -70,7 +71,11 @@ certs.removeAt(0); SignatureChecker checker(certs); + QTest::ignoreMessage(QtCriticalMsg, "Certificate verification failed: \"DETESTeID00002\""); QVERIFY(!checker.check()); +#if OPENSSL_VERSION_NUMBER >= 0x30500010L + QCOMPARE(getOpenSslError(), QByteArray("error:030000EA:digital envelope routines::provider signature failure")); +#endif } @@ -82,6 +87,7 @@ certs.removeAt(0); SignatureChecker checker(certs); + QTest::ignoreMessage(QtCriticalMsg, "No elliptic curve parameters"); QVERIFY(!checker.check()); } @@ -92,7 +98,11 @@ certs.removeAt(2); SignatureChecker checker(certs); + QTest::ignoreMessage(QtCriticalMsg, "Certificate verification failed: \"DEDVeIDDPST00035\""); QVERIFY(!checker.check()); +#if OPENSSL_VERSION_NUMBER >= 0x30500010L + QCOMPARE(getOpenSslError(), QByteArray("error:030000EA:digital envelope routines::provider signature failure")); +#endif } @@ -109,14 +119,14 @@ { QTest::ignoreMessage(QtCriticalMsg, "Cannot fetch signing key"); const auto& certificate = load(":/card/cvat-DEDEMODEV00038.hex"_L1); - SignatureChecker::checkSignature(nullptr, certificate, &certificate->getBody().getPublicKey()); + QVERIFY(!SignatureChecker::checkSignature(nullptr, certificate, &certificate->getBody().getPublicKey())); QTest::ignoreMessage(QtCriticalMsg, "Missing signing key"); - SignatureChecker::checkSignature(nullptr, QByteArray(), QByteArray(), QCryptographicHash::Algorithm::Sha256); + QVERIFY(!SignatureChecker::checkSignature(nullptr, QByteArray(), QByteArray(), QCryptographicHash::Algorithm::Sha256)); QTest::ignoreMessage(QtCriticalMsg, "Cannot init verify ctx"); QSharedPointer key(EVP_PKEY_new(), [](EVP_PKEY* pKey){EVP_PKEY_free(pKey);}); - SignatureChecker::checkSignature(key, QByteArray(), QByteArray(), QCryptographicHash::Algorithm::Sha256); + QVERIFY(!SignatureChecker::checkSignature(key, QByteArray(), QByteArray(), QCryptographicHash::Algorithm::Sha256)); #if OPENSSL_VERSION_NUMBER < 0x30000000L QCOMPARE(getOpenSslError(), QByteArray("error:0609D09C:digital envelope routines:int_ctx_new:unsupported algorithm | error:0608F096:digital envelope routines:EVP_PKEY_verify_init:operation not supported for this keytype")); #else diff -Nru ausweisapp2-2.3.1/test/qt/card/base/apdu/test_GeneralAuthenticateResponse.cpp ausweisapp2-2.4.0/test/qt/card/base/apdu/test_GeneralAuthenticateResponse.cpp --- ausweisapp2-2.3.1/test/qt/card/base/apdu/test_GeneralAuthenticateResponse.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/apdu/test_GeneralAuthenticateResponse.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -23,7 +23,7 @@ const GAEncryptedNonceResponse response(apdu); - QCOMPARE(response.getEncryptedNonce(), QByteArray::fromHex("5391DED7867C2d7DF7F871ED6913C07D")); + QCOMPARE(response.getEncryptedNonce(), QByteArray::fromHex("5391DED7867C2d7DF7F871ED6913C07D")); // codespell:ignore } @@ -79,11 +79,11 @@ void parseGAMutualAuthenticationResponse_withoutCARs() { - const ResponseApdu apdu(QByteArray::fromHex("7C0A8608AFCD013365384BA39000")); + const ResponseApdu apdu(QByteArray::fromHex("7C0A8608AFCD013365384BA39000")); // codespell:ignore const GAMutualAuthenticationResponse response(apdu); - QCOMPARE(response.getAuthenticationToken(), QByteArray::fromHex("AFCD013365384BA3")); + QCOMPARE(response.getAuthenticationToken(), QByteArray::fromHex("AFCD013365384BA3")); // codespell:ignore QCOMPARE(response.getCarCurr(), QByteArray()); QCOMPARE(response.getCarPrev(), QByteArray()); } diff -Nru ausweisapp2-2.3.1/test/qt/card/base/apdu/test_PacePinStatus.cpp ausweisapp2-2.4.0/test/qt/card/base/apdu/test_PacePinStatus.cpp --- ausweisapp2-2.3.1/test/qt/card/base/apdu/test_PacePinStatus.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/apdu/test_PacePinStatus.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -63,7 +63,7 @@ QTest::newRow("FILE_NOT_FOUND") << StatusCode::FILE_NOT_FOUND << -1 << false << false; QTest::newRow("RECORD_NOT_FOUND") << StatusCode::RECORD_NOT_FOUND << -1 << false << false; QTest::newRow("INVALID_PARAMETER") << StatusCode::INVALID_PARAMETER << -1 << false << false; - QTest::newRow("LC_INCONSISTANT") << StatusCode::LC_INCONSISTANT << -1 << false << false; + QTest::newRow("LC_INCONSISTENT") << StatusCode::LC_INCONSISTENT << -1 << false << false; QTest::newRow("REFERENCED_DATA_NOT_FOUND") << StatusCode::REFERENCED_DATA_NOT_FOUND << -1 << false << false; QTest::newRow("ILLEGAL_OFFSET") << StatusCode::ILLEGAL_OFFSET << -1 << false << false; QTest::newRow("UNSUPPORTED_CLA") << StatusCode::UNSUPPORTED_CLA << -1 << false << false; diff -Nru ausweisapp2-2.3.1/test/qt/card/base/apdu/test_ResponseApdu.cpp ausweisapp2-2.4.0/test/qt/card/base/apdu/test_ResponseApdu.cpp --- ausweisapp2-2.3.1/test/qt/card/base/apdu/test_ResponseApdu.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/apdu/test_ResponseApdu.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -118,7 +118,7 @@ QTest::newRow("FILE_NOT_FOUND") << StatusCode::FILE_NOT_FOUND << SW1::WRONG_PARAMETERS_P1_P2 << static_cast(0x82); QTest::newRow("RECORD_NOT_FOUND") << StatusCode::RECORD_NOT_FOUND << SW1::WRONG_PARAMETERS_P1_P2 << static_cast(0x83); QTest::newRow("INVALID_PARAMETER") << StatusCode::INVALID_PARAMETER << SW1::WRONG_PARAMETERS_P1_P2 << static_cast(0x86); - QTest::newRow("LC_INCONSISTANT") << StatusCode::LC_INCONSISTANT << SW1::WRONG_PARAMETERS_P1_P2 << static_cast(0x87); + QTest::newRow("LC_INCONSISTENT") << StatusCode::LC_INCONSISTENT << SW1::WRONG_PARAMETERS_P1_P2 << static_cast(0x87); QTest::newRow("REFERENCED_DATA_NOT_FOUND") << StatusCode::REFERENCED_DATA_NOT_FOUND << SW1::WRONG_PARAMETERS_P1_P2 << static_cast(0x88); QTest::newRow("ILLEGAL_OFFSET") << StatusCode::ILLEGAL_OFFSET << SW1::WRONG_PARAMETERS_P1_P2_NO_INFO << static_cast(0x00); QTest::newRow("UNSUPPORTED_CLA") << StatusCode::UNSUPPORTED_CLA << SW1::CLASS_NOT_SUPPORTED << static_cast(0x00); diff -Nru ausweisapp2-2.3.1/test/qt/card/base/apdu/test_SecureMessaging.cpp ausweisapp2-2.4.0/test/qt/card/base/apdu/test_SecureMessaging.cpp --- ausweisapp2-2.3.1/test/qt/card/base/apdu/test_SecureMessaging.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/apdu/test_SecureMessaging.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -172,7 +172,7 @@ } - void testExtendedLengthAfterEcryption() + void testExtendedLengthAfterEncryption() { const QByteArray data(250, 0x42); const CommandApdu command(Ins::MSE_SET, 0x20, 0x30, data, 24); @@ -316,7 +316,7 @@ } - void testMaximumLengthExeeded() + void testMaximumLengthExceeded() { CommandApdu apdu = CommandApdu(QByteArray::fromHex("00010203"), QByteArray(65535, 0x42), 1); QTest::ignoreMessage(QtCriticalMsg, "Command data exceeds maximum of 0xffff"); diff -Nru ausweisapp2-2.3.1/test/qt/card/base/command/test_TransmitCommand.cpp ausweisapp2-2.4.0/test/qt/card/base/command/test_TransmitCommand.cpp --- ausweisapp2-2.3.1/test/qt/card/base/command/test_TransmitCommand.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/command/test_TransmitCommand.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -105,7 +105,7 @@ } - void test_InternalExecuteUnsuccesfulSingleCommand() + void test_InternalExecuteUnsuccessfulSingleCommand() { QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); diff -Nru ausweisapp2-2.3.1/test/qt/card/base/command/test_UpdRetryCounterCommand.cpp ausweisapp2-2.4.0/test/qt/card/base/command/test_UpdRetryCounterCommand.cpp --- ausweisapp2-2.3.1/test/qt/card/base/command/test_UpdRetryCounterCommand.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/command/test_UpdRetryCounterCommand.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -24,7 +24,7 @@ { MockReader reader("name"_L1); QSharedPointer worker(new MockCardConnectionWorker(&reader)); - UpdateRetryCounterCommand command(worker); + UpdateRetryCounterCommand command(worker, QStringLiteral("slotname")); command.internalExecute(); QCOMPARE(command.getReturnCode(), CardReturnCode::CARD_NOT_FOUND); } @@ -37,12 +37,22 @@ reader.setInfoCardInfo(info); QSharedPointer worker(new MockCardConnectionWorker(&reader)); - UpdateRetryCounterCommand command(worker); + UpdateRetryCounterCommand command(worker, QStringLiteral("slotname")); command.internalExecute(); QCOMPARE(command.getReturnCode(), CardReturnCode::OK); } + void test_SlotHandle() + { + MockReader reader("name"_L1); + QSharedPointer worker(new MockCardConnectionWorker(&reader)); + const QString name = QStringLiteral("slotname"); + UpdateRetryCounterCommand command(worker, name); + QCOMPARE(command.getSlotHandle(), name); + } + + }; QTEST_GUILESS_MAIN(test_UpdateRetryCounterCommand) diff -Nru ausweisapp2-2.3.1/test/qt/card/base/pinpad/test_EstablishPaceChannelOutput.cpp ausweisapp2-2.4.0/test/qt/card/base/pinpad/test_EstablishPaceChannelOutput.cpp --- ausweisapp2-2.3.1/test/qt/card/base/pinpad/test_EstablishPaceChannelOutput.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/pinpad/test_EstablishPaceChannelOutput.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -93,7 +93,7 @@ "0000"); EstablishPaceChannelOutput channelOutput; - QVERIFY(channelOutput.parse(bytes)); + QVERIFY(!channelOutput.parse(bytes)); QCOMPARE(channelOutput.getPaceReturnCode(), CardReturnCode::INVALID_PASSWORD); QCOMPARE(channelOutput.getStatusMseSetAt(), QByteArray::fromHex("0000")); diff -Nru ausweisapp2-2.3.1/test/qt/card/base/pinpad/test_PinModifyOutput.cpp ausweisapp2-2.4.0/test/qt/card/base/pinpad/test_PinModifyOutput.cpp --- ausweisapp2-2.3.1/test/qt/card/base/pinpad/test_PinModifyOutput.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/pinpad/test_PinModifyOutput.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -/** - * Copyright (c) 2014-2025 Governikus GmbH & Co. KG, Germany - */ - -#include -#include - - -#include "pinpad/PinModifyOutput.h" - - -using namespace governikus; - - -class test_PinModifyOutput - : public QObject -{ - Q_OBJECT - - private Q_SLOTS: - void check_data() - { - QTest::addColumn("hexData"); - - QTest::newRow("UNKNOWN1") << QByteArray("0200"); - QTest::newRow("UNKNOWN2") << QByteArray("6982"); - QTest::newRow("UNKNOWN3") << QByteArray("beef"); - QTest::newRow("INPUT_TIME_OUT") << QByteArray("6400"); - QTest::newRow("CANCELLATION_BY_USER") << QByteArray("6401"); - QTest::newRow("NEW_PIN_MISMATCH") << QByteArray("6402"); - QTest::newRow("NEW_PIN_INVALID_LENGTH") << QByteArray("6403"); - QTest::newRow("COMMAND_FAILED") << QByteArray("6a80"); - QTest::newRow("OK") << QByteArray("9000"); - - } - - - void check() - { - QFETCH(QByteArray, hexData); - - const auto data = QByteArray::fromHex(hexData); - PinModifyOutput output((ResponseApdu(data))); - - QCOMPARE(output.getResponseApdu(), data); - QCOMPARE(output.toCcid(), data); - } - - -}; - -QTEST_GUILESS_MAIN(test_PinModifyOutput) -#include "test_PinModifyOutput.moc" diff -Nru ausweisapp2-2.3.1/test/qt/card/base/test_Card.cpp ausweisapp2-2.4.0/test/qt/card/base/test_Card.cpp --- ausweisapp2-2.3.1/test/qt/card/base/test_Card.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/test_Card.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -60,17 +60,19 @@ void test_generateErrorMessage_data() { + const QString emptyStr = QLatin1String(""); + QTest::addColumn("sdk"); QTest::addColumn("message"); QTest::addColumn("expectedMessage"); QTest::addRow("Normal message") << false << "specific message" << "specific message"; QTest::addRow("Null message") << false << QString() << QString(); - QTest::addRow("Empty message") << false << QStringLiteral("") << QStringLiteral(""); + QTest::addRow("Empty message") << false << emptyStr << emptyStr; QTest::addRow("SDK - Normal message") << true << "specific message" << "scan failed"; QTest::addRow("SDK - Null message") << true << QString() << QString(); - QTest::addRow("SDK - Empty message") << true << QStringLiteral("") << "scan failed"; + QTest::addRow("SDK - Empty message") << true << emptyStr << "scan failed"; } diff -Nru ausweisapp2-2.3.1/test/qt/card/base/test_CardConnection.cpp ausweisapp2-2.4.0/test/qt/card/base/test_CardConnection.cpp --- ausweisapp2-2.3.1/test/qt/card/base/test_CardConnection.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/test_CardConnection.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -35,9 +35,11 @@ QSharedPointer worker(new MockCardConnectionWorker()); worker->moveToThread(&workerThread); CardConnection connection(worker); - UpdateRetryCounterCommand* command = connection.createUpdateRetryCounterCommand(); + const QString slotHandle = QStringLiteral("slot handle"); + UpdateRetryCounterCommand* command = connection.createUpdateRetryCounterCommand(slotHandle); command->deleteLater(); QCOMPARE(command->mCardConnectionWorker, worker); + QCOMPARE(command->getSlotHandle(), slotHandle); } diff -Nru ausweisapp2-2.3.1/test/qt/card/base/test_CardConnectionWorker.cpp ausweisapp2-2.4.0/test/qt/card/base/test_CardConnectionWorker.cpp --- ausweisapp2-2.3.1/test/qt/card/base/test_CardConnectionWorker.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/test_CardConnectionWorker.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -9,6 +9,7 @@ #include +using namespace Qt::Literals::StringLiterals; using namespace governikus; @@ -31,7 +32,7 @@ private Q_SLOTS: void init() { - mReader.reset(new MockReader()); + mReader.reset(new MockReader(QStringLiteral("Reader"), ReaderManagerPluginType::PCSC)); mWorker = CardConnectionWorker::create(mReader.data()); } @@ -58,8 +59,40 @@ } + void test_EstablishPaceChannel_data() + { + QTest::addColumn("retryCounter"); + QTest::addColumn("pacePasswordId"); + QTest::addColumn("cardReturnCode"); + + QTest::newRow("PIN RT 3") << 3 << PacePasswordId::PACE_PIN << CardReturnCode::INVALID_PIN; + QTest::newRow("PIN RT 2") << 2 << PacePasswordId::PACE_PIN << CardReturnCode::INVALID_PIN_2; + QTest::newRow("PIN RT 1") << 1 << PacePasswordId::PACE_PIN << CardReturnCode::INVALID_PIN_3; + QTest::newRow("PIN RT 0") << 0 << PacePasswordId::PACE_PIN << CardReturnCode::INVALID_PIN; + + QTest::newRow("CAN RT 3") << 3 << PacePasswordId::PACE_CAN << CardReturnCode::INVALID_CAN; + QTest::newRow("CAN RT 2") << 2 << PacePasswordId::PACE_CAN << CardReturnCode::INVALID_CAN; + QTest::newRow("CAN RT 1") << 1 << PacePasswordId::PACE_CAN << CardReturnCode::INVALID_CAN; + QTest::newRow("CAN RT 0") << 0 << PacePasswordId::PACE_CAN << CardReturnCode::INVALID_CAN; + + QTest::newRow("PUK RT 3") << 3 << PacePasswordId::PACE_PUK << CardReturnCode::INVALID_PUK; + QTest::newRow("PUK RT 2") << 2 << PacePasswordId::PACE_PUK << CardReturnCode::INVALID_PUK; + QTest::newRow("PUK RT 1") << 1 << PacePasswordId::PACE_PUK << CardReturnCode::INVALID_PUK; + QTest::newRow("PUK RT 0") << 0 << PacePasswordId::PACE_PUK << CardReturnCode::INVALID_PUK; + + QTest::newRow("MRZ RT 3") << 3 << PacePasswordId::PACE_MRZ << CardReturnCode::UNKNOWN; + QTest::newRow("MRZ RT 2") << 2 << PacePasswordId::PACE_MRZ << CardReturnCode::UNKNOWN; + QTest::newRow("MRZ RT 1") << 1 << PacePasswordId::PACE_MRZ << CardReturnCode::UNKNOWN; + QTest::newRow("MRZ RT 0") << 0 << PacePasswordId::PACE_MRZ << CardReturnCode::UNKNOWN; + } + + void test_EstablishPaceChannel() { + QFETCH(int, retryCounter); + QFETCH(PacePasswordId, pacePasswordId); + QFETCH(CardReturnCode, cardReturnCode); + const QByteArray password("111111"); const QByteArray chat = QByteArray::fromHex("7F4C12060904007F00070301020253050000000F0F"); const QByteArray certDescription = QByteArray::fromHex("30 8202A4" @@ -74,17 +107,31 @@ setCard(); + QByteArray startMsg("Starting PACE for %1"); + startMsg.replace("%1"_ba, Enum::getName(pacePasswordId)); + + QByteArray basicMsg("Finished PACE for %1 with result PROTOCOL_ERROR"); + basicMsg.replace("%1"_ba, Enum::getName(pacePasswordId)); + //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); + QTest::ignoreMessage(QtInfoMsg, startMsg.data()); + QTest::ignoreMessage(QtInfoMsg, basicMsg.data()); + QCOMPARE(mWorker->establishPaceChannel(pacePasswordId, password, chat, certDescription).getPaceReturnCode(), CardReturnCode::PROTOCOL_ERROR); + + QByteArray comfortMsg("Finished PACE for %1 with result %2"); + comfortMsg.replace("%1"_ba, Enum::getName(pacePasswordId)); + comfortMsg.replace("%2"_ba, Enum::getName(cardReturnCode)); //comfort reader mReader->setInfoBasicReader(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, QByteArray(), chat, certDescription).getPaceReturnCode(), CardReturnCode::COMMAND_FAILED); + mReader->setInfoCardInfo(CardInfo(CardType::EID_CARD, FileRef(), QSharedPointer(), retryCounter)); + QTest::ignoreMessage(QtInfoMsg, startMsg.data()); + if (pacePasswordId == PacePasswordId::PACE_PIN) + { + QTest::ignoreMessage(QtWarningMsg, "Add missing StatusCodeMseSet in EstablishPaceChannelOutput for Reiner SCT reader with pin pad that do not follow PCSC Part 10 IFDs with Secure PIN Entry Capabilities - AMENDMENT 1.1"); + } + QTest::ignoreMessage(QtInfoMsg, comfortMsg.data()); + QCOMPARE(mWorker->establishPaceChannel(pacePasswordId, QByteArray(), chat, certDescription).getPaceReturnCode(), cardReturnCode); } diff -Nru ausweisapp2-2.3.1/test/qt/card/base/test_CardInfo.cpp ausweisapp2-2.4.0/test/qt/card/base/test_CardInfo.cpp --- ausweisapp2-2.3.1/test/qt/card/base/test_CardInfo.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/test_CardInfo.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -95,7 +95,7 @@ } - void test_RetryCounterDeterminated() + void test_RetryCounterDetermined() { const CardInfo info1(CardType::EID_CARD); QVERIFY(!info1.isRetryCounterDetermined()); diff -Nru ausweisapp2-2.3.1/test/qt/card/base/test_ReaderManager.cpp ausweisapp2-2.4.0/test/qt/card/base/test_ReaderManager.cpp --- ausweisapp2-2.3.1/test/qt/card/base/test_ReaderManager.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/test_ReaderManager.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -9,15 +9,20 @@ #include #include #include +#include #include + Q_IMPORT_PLUGIN(MockReaderManagerPlugin) + using namespace Qt::Literals::StringLiterals; using namespace governikus; + Q_DECLARE_METATYPE(ReaderInfo) + class CreateCardConnectionCommandSlot : public QObject { @@ -46,6 +51,8 @@ private Q_SLOTS: void initTestCase() { + QThread::currentThread()->setObjectName(QStringLiteral("MainThread")); + auto* readerManager = Env::getSingleton(); QSignalSpy spy(readerManager, &ReaderManager::fireInitialized); readerManager->init(); @@ -90,6 +97,35 @@ } + void callExecute() + { + auto* readerManager = Env::getSingleton(); + + QTest::ignoreMessage(QtDebugMsg, "Name of the main thread: MainThread"); + qDebug().noquote() << "Name of the main thread:" << QThread::currentThread()->objectName(); + + QTest::ignoreMessage(QtDebugMsg, "callExecute from MainThread. Executed in ReaderManagerThread"); + QTest::ignoreMessage(QtDebugMsg, "callExecute from ReaderManagerThread. Executed in ReaderManagerThread"); + readerManager->callExecute([readerManager] { + qDebug().noquote() << "callExecute from MainThread. Executed in" << QThread::currentThread()->objectName(); + + readerManager->callExecute([] { + qDebug().noquote() << "callExecute from ReaderManagerThread. Executed in" << QThread::currentThread()->objectName(); + }); + }); + + readerManager->shutdown(); + QTest::ignoreMessage(QtWarningMsg, "Cannot call Execute if ReaderManager-Thread is not active"); + readerManager->callExecute([] { + qDebug() << "callExecute without ReaderManagerThread"; + }); + + QSignalSpy spy(readerManager, &ReaderManager::fireInitialized); + readerManager->init(); + QTRY_COMPARE(spy.count(), 1); // clazy:exclude=qstring-allocations + } + + void fireCreateCardConnection_forUnknownReader() { CreateCardConnectionCommandSlot commandSlot; @@ -116,7 +152,7 @@ void fireCreateCardConnection_cardConnectFail() { CreateCardConnectionCommandSlot commandSlot; - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 4711"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 4711"_L1); MockCardConfig cardConfig; cardConfig.mConnect = CardReturnCode::COMMAND_FAILED; reader->setCard(cardConfig); @@ -131,7 +167,7 @@ void fireCreateCardConnection() { CreateCardConnectionCommandSlot commandSlot; - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 4711"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 4711"_L1); QList transmitConfigs; transmitConfigs.append(TransmitConfig(CardReturnCode::OK, QByteArray::fromHex("6982"))); transmitConfigs.append(TransmitConfig(CardReturnCode::OK, QByteArray::fromHex("9000"))); @@ -149,7 +185,7 @@ void fireCreateCardConnectionFailedSelectApplication() { CreateCardConnectionCommandSlot commandSlot; - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 4711"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 4711"_L1); QList transmitConfigs; transmitConfigs.append(TransmitConfig(CardReturnCode::OK, QByteArray::fromHex("6A82"))); MockCardConfig cardConfig(transmitConfigs); @@ -250,7 +286,7 @@ void test_shelve() { - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1); reader->setInfoCardInfo(CardInfo(CardType::EID_CARD)); QSignalSpy removed(reader, &Reader::fireCardRemoved); @@ -259,7 +295,7 @@ QVERIFY(reader->getReaderInfo().hasCard()); QVERIFY(!reader->getReaderInfo().isInsertable()); - MockReaderManagerPlugin::getInstance().shelve(); + MockReaderManagerPlugin::getInstance().shelveAll(); QCOMPARE(removed.count(), 0); QCOMPARE(properties.count(), 0); QVERIFY(reader->getReaderInfo().hasCard()); @@ -272,7 +308,7 @@ QVERIFY(reader->getReaderInfo().hasCard()); QVERIFY(reader->getReaderInfo().isInsertable()); - MockReaderManagerPlugin::getInstance().shelve(); + MockReaderManagerPlugin::getInstance().shelveAll(); QCOMPARE(removed.count(), 1); QCOMPARE(properties.count(), 1); QVERIFY(!reader->getReaderInfo().hasCard()); diff -Nru ausweisapp2-2.3.1/test/qt/card/base/test_ReaderManagerPlugin.cpp ausweisapp2-2.4.0/test/qt/card/base/test_ReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/test/qt/card/base/test_ReaderManagerPlugin.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/base/test_ReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#include "ReaderManagerPlugin.h" + +#include +#include + + +using namespace Qt::Literals::StringLiterals; +using namespace governikus; + + +class SimpleReaderManagerPlugin + : public ReaderManagerPlugin +{ + Q_OBJECT + + public: + using ReaderManagerPlugin::setPluginAvailable; + + SimpleReaderManagerPlugin() + : ReaderManagerPlugin(ReaderManagerPluginType::MOCK) + { + } + + + [[nodiscard]] QPointer getReader(const QString& pReaderName) const override + { + Q_UNUSED(pReaderName) + return nullptr; + } + + + void shelveAll() const override + { + } + + +}; + + +class test_ReaderManagerPlugin + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void setPluginAvailable() + { + SimpleReaderManagerPlugin plugin; + QSignalSpy spy(&plugin, &ReaderManagerPlugin::fireStatusChanged); + QCOMPARE(plugin.getInfo().isAvailable(), false); + + plugin.setPluginAvailable(false); + QCOMPARE(plugin.getInfo().isAvailable(), false); + QCOMPARE(spy.count(), 1); + + plugin.setPluginAvailable(true); + QCOMPARE(plugin.getInfo().isAvailable(), true); + QCOMPARE(spy.count(), 2); + + plugin.setPluginAvailable(true); + QCOMPARE(plugin.getInfo().isAvailable(), true); + QCOMPARE(spy.count(), 3); + } + + + void reset() + { + SimpleReaderManagerPlugin plugin; + + QTest::ignoreMessage(QtDebugMsg, "Shutdown ReaderManagerPluginType::MOCK"); + plugin.reset(); + } + + + void insert() + { + SimpleReaderManagerPlugin plugin; + + QTest::ignoreMessage(QtDebugMsg, "insert is not supported by ReaderManagerPluginType::MOCK"); + plugin.insert("Card"_L1, QVariant()); + } + + +}; + +QTEST_GUILESS_MAIN(test_ReaderManagerPlugin) +#include "test_ReaderManagerPlugin.moc" diff -Nru ausweisapp2-2.3.1/test/qt/card/ifd/test_IfdCard.cpp ausweisapp2-2.4.0/test/qt/card/ifd/test_IfdCard.cpp --- ausweisapp2-2.3.1/test/qt/card/ifd/test_IfdCard.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/ifd/test_IfdCard.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -100,6 +100,24 @@ } + void respectRemoveCard() + { + IfdCard card(mDispatcher, QStringLiteral("IfdReader")); + + mDispatcher->setState(MockIfdDispatcher::DispatcherState::ReaderWithCard); + QCOMPARE(card.establishPaceChannel(PacePasswordId::PACE_PIN, 6, QByteArray(), QByteArray()), EstablishPaceChannelOutput(CardReturnCode::OK)); + QCOMPARE(card.setEidPin(1).mReturnCode, CardReturnCode::OK); + + mDispatcher->setState(MockIfdDispatcher::DispatcherState::ReaderWithCardRemoved); + QTest::ignoreMessage(QtDebugMsg, "Card was removed while waiting for IFDEstablishPACEChannelResponse"); + QTest::ignoreMessage(QtWarningMsg, "Ignoring unexpected message type: IFDStatus"); + QCOMPARE(card.establishPaceChannel(PacePasswordId::PACE_PIN, 6, QByteArray(), QByteArray()), EstablishPaceChannelOutput(CardReturnCode::CARD_NOT_FOUND)); + QTest::ignoreMessage(QtDebugMsg, "Card was removed while waiting for IFDModifyPINResponse"); + QTest::ignoreMessage(QtWarningMsg, "Ignoring unexpected message type: IFDStatus"); + QCOMPARE(card.setEidPin(1).mReturnCode, CardReturnCode::CARD_NOT_FOUND); + } + + }; QTEST_GUILESS_MAIN(test_IfdCard) diff -Nru ausweisapp2-2.3.1/test/qt/card/pace/test_PaceHandler.cpp ausweisapp2-2.4.0/test/qt/card/pace/test_PaceHandler.cpp --- ausweisapp2-2.3.1/test/qt/card/pace/test_PaceHandler.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/pace/test_PaceHandler.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -30,7 +30,7 @@ } - void getPaceProtocol_uninitiaized() + void getPaceProtocol_uninitialized() { QScopedPointer paceHandler(new PaceHandler(QSharedPointer())); QVERIFY(paceHandler->getPaceProtocol().getOid().isUndefined()); diff -Nru ausweisapp2-2.3.1/test/qt/card/pcsc/test_PcscReaderManagerPlugin.cpp ausweisapp2-2.4.0/test/qt/card/pcsc/test_PcscReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/test/qt/card/pcsc/test_PcscReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/pcsc/test_PcscReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -25,13 +25,15 @@ PcscReaderManagerPlugin plugin; QCOMPARE(plugin.objectName(), QStringLiteral("PcscReaderManager")); - QVERIFY(plugin.getReaders().isEmpty()); + QVERIFY(plugin.mReaders.isEmpty()); + QCOMPARE(plugin.getReader(QStringLiteral("PcscReaderManager")), nullptr); QSignalSpy spyAdded(&plugin, &PcscReaderManagerPlugin::fireReaderAdded); QTest::ignoreMessage(QtDebugMsg, QStringLiteral("fireReaderAdded: \"%1\" ( 1 reader in total )").arg(readerName).toUtf8().data()); plugin.addReaders({readerName}); QCOMPARE(spyAdded.size(), 1); - QCOMPARE(plugin.getReaders().size(), 1); + QCOMPARE(plugin.mReaders.size(), 1); + QVERIFY(plugin.getReader(readerName) != nullptr); } diff -Nru ausweisapp2-2.3.1/test/qt/card/simulator/test_SimulatorReaderManagerPlugin.cpp ausweisapp2-2.4.0/test/qt/card/simulator/test_SimulatorReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/test/qt/card/simulator/test_SimulatorReaderManagerPlugin.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/simulator/test_SimulatorReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#include "SimulatorReaderManagerPlugin.h" + +#include "AppSettings.h" +#include "VolatileSettings.h" + +#include + + +using namespace Qt::Literals::StringLiterals; +using namespace governikus; + + +constexpr QLatin1StringView readerName("Simulator"); + + +class test_SimulatorReaderManagerPlugin + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void cleanup() + { + Env::getSingleton()->setUsedAsSDK(true); + Env::getSingleton()->getSimulatorSettings().setEnabled(false); + } + + + void initialization() + { + SimulatorReaderManagerPlugin plugin; + QSignalSpy spyStatus(&plugin, &ReaderManagerPlugin::fireStatusChanged); + + QCOMPARE(plugin.getInfo().getPluginType(), ReaderManagerPluginType::SIMULATOR); + QCOMPARE(plugin.getInfo().isAvailable(), true); + QCOMPARE(plugin.getInfo().isEnabled(), false); + + QTest::ignoreMessage(QtDebugMsg, "SimulatorStateChanged: true"); + plugin.init(); + QCOMPARE(spyStatus.count(), 1); + QCOMPARE(plugin.getInfo().isEnabled(), true); + } + + + void getReader() + { + SimulatorReaderManagerPlugin plugin; + QCOMPARE(plugin.getReader(readerName), nullptr); + + plugin.init(); + QCOMPARE(plugin.getReader(readerName), nullptr); + + plugin.startScan(true); + QVERIFY(plugin.getReader(readerName)); + QCOMPARE(plugin.getReader("wrongName"_L1), nullptr); + } + + + void settings() + { + SimulatorReaderManagerPlugin plugin; + QSignalSpy spyAdded(&plugin, &SimulatorReaderManagerPlugin::fireReaderAdded); + QSignalSpy spyRemoved(&plugin, &SimulatorReaderManagerPlugin::fireReaderRemoved); + auto& simulatorSettings = Env::getSingleton()->getSimulatorSettings(); + QCOMPARE(simulatorSettings.isEnabled(), false); + auto* volatileSettings = Env::getSingleton(); + QCOMPARE(volatileSettings->isUsedAsSDK(), true); + + volatileSettings->setUsedAsSDK(false); + QCOMPARE(simulatorSettings.isEnabled(), false); + + plugin.init(); + QCOMPARE(simulatorSettings.isEnabled(), false); + + QTest::ignoreMessage(QtDebugMsg, "SimulatorStateChanged: true"); + simulatorSettings.setEnabled(true); + QCOMPARE(spyAdded.count(), 0); + + QTest::ignoreMessage(QtDebugMsg, "SimulatorStateChanged: false"); + simulatorSettings.setEnabled(false); + QCOMPARE(spyRemoved.count(), 0); + + simulatorSettings.setEnabled(true); + plugin.startScan(true); + QCOMPARE(spyAdded.count(), 1); + + simulatorSettings.setEnabled(false); + QCOMPARE(spyRemoved.count(), 1); + } + + + void shelve() + { + SimulatorReaderManagerPlugin plugin; + plugin.shelveAll(); + + plugin.init(); + plugin.shelveAll(); + + QTest::ignoreMessage(QtDebugMsg, "Card shelved"); + plugin.startScan(true); + + const auto& reader = plugin.getReader(readerName); + QSignalSpy spyInserted(reader, &Reader::fireCardInserted); + QSignalSpy spyRemoved(reader, &Reader::fireCardRemoved); + plugin.insert(readerName, QVariant()); + QCOMPARE(spyInserted.count(), 1); + + QTest::ignoreMessage(QtDebugMsg, "Card shelved"); + plugin.shelveAll(); + QCOMPARE(spyRemoved.count(), 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_SimulatorReaderManagerPlugin) +#include "test_SimulatorReaderManagerPlugin.moc" diff -Nru ausweisapp2-2.3.1/test/qt/card/smart/test_SmartReaderManagerPlugin.cpp ausweisapp2-2.4.0/test/qt/card/smart/test_SmartReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/test/qt/card/smart/test_SmartReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/card/smart/test_SmartReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -43,7 +43,7 @@ SmartReaderManagerPlugin plugin; plugin.init(); QCOMPARE(plugin.getInfo().isAvailable(), false); - QCOMPARE(plugin.getReaders().size(), 0); + QCOMPARE(plugin.mReader, nullptr); }); } @@ -59,7 +59,7 @@ QSignalSpy readerRemovedSpy(&plugin, &SmartReaderManagerPlugin::fireReaderRemoved); auto& settings = Env::getSingleton()->getGeneralSettings(); - QCOMPARE(plugin.getReaders().size(), 0); + QCOMPARE(plugin.mReader, nullptr); settings.setSmartAvailable(true); QCOMPARE(readerAddedSpy.count(), 1); @@ -68,8 +68,7 @@ QCOMPARE(readerAddedInfo.isValid(), true); QCOMPARE(readerUpdatedSpy.count(), 0); QCOMPARE(readerRemovedSpy.count(), 0); - QCOMPARE(plugin.getReaders().size(), 1); - QVERIFY(plugin.getReaders().at(0) != nullptr); + QVERIFY(plugin.mReader != nullptr); settings.setSmartAvailable(false); QCOMPARE(readerAddedSpy.count(), 1); @@ -78,8 +77,7 @@ QCOMPARE(readerUpdatedInfo1.getName(), QStringLiteral("Smart")); QCOMPARE(readerUpdatedInfo1.isValid(), false); QCOMPARE(readerRemovedSpy.count(), 0); - QCOMPARE(plugin.getReaders().size(), 0); - + QCOMPARE(plugin.mReader, nullptr); settings.setSmartAvailable(true); QCOMPARE(readerAddedSpy.count(), 1); QCOMPARE(readerUpdatedSpy.count(), 2); @@ -87,8 +85,7 @@ QCOMPARE(readerUpdatedInfo2.getName(), QStringLiteral("Smart")); QCOMPARE(readerUpdatedInfo2.isValid(), true); QCOMPARE(readerRemovedSpy.count(), 0); - QCOMPARE(plugin.getReaders().size(), 1); - QVERIFY(plugin.getReaders().at(0) != nullptr); + QVERIFY(plugin.mReader != nullptr); }); } diff -Nru ausweisapp2-2.3.1/test/qt/configuration/test_ProviderConfiguration.cpp ausweisapp2-2.4.0/test/qt/configuration/test_ProviderConfiguration.cpp --- ausweisapp2-2.3.1/test/qt/configuration/test_ProviderConfiguration.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/configuration/test_ProviderConfiguration.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -28,9 +28,9 @@ void testProviderUrls() { const ProviderConfigurationInfo provider1({ - /* short name */ QString(), - /* long name */ QString(), - /* long description */ QString(), + /* short name */ LanguageString(), + /* long name */ LanguageString(), + /* long description */ LanguageString(), /* address */ QStringLiteral("ftp://homepage.com/form"), /* homepage */ QStringLiteral("ftp://www.homepage.de/bla/bla1"), QString(), QString(), QString(), QString(), QString(), QString(), {}, @@ -39,9 +39,9 @@ QCOMPARE(provider1.getHomepageBase(), QStringLiteral("www.homepage.de")); const ProviderConfigurationInfo provider2({ - /* short name */ QString(), - /* long name */ QString(), - /* long description */ QString(), + /* short name */ LanguageString(), + /* long name */ LanguageString(), + /* long description */ LanguageString(), /* address */ QStringLiteral("https://homepage.com/form"), /* homepage */ QStringLiteral("https://www.homepage.de/bla/bla1"), QString(), QString(), QString(), QString(), QString(), QString(), {}, @@ -50,9 +50,9 @@ QCOMPARE(provider2.getHomepageBase(), QStringLiteral("www.homepage.de")); const ProviderConfigurationInfo provider3({ - /* short name */ QString(), - /* long name */ QString(), - /* long description */ QString(), + /* short name */ LanguageString(), + /* long name */ LanguageString(), + /* long description */ LanguageString(), /* address */ QStringLiteral("homepage.com/form"), /* homepage */ QStringLiteral("www.homepage.de/bla/bla1"), QString(), QString(), QString(), QString(), QString(), QString(), {}, @@ -70,9 +70,9 @@ { // Add image and icon. const ProviderConfigurationInfo provider({ - /* short name */ QStringLiteral("Provider 1"), - /* long name */ QStringLiteral("Provider 1 - long name"), - /* long description */ QStringLiteral("Provider description long"), + /* short name */ LanguageString(QStringLiteral("Provider 1")), + /* long name */ LanguageString(QStringLiteral("Provider 1 - long name")), + /* long description */ LanguageString(QStringLiteral("Provider description long")), /* address */ QStringLiteral("https://www.homepage.com/form/"), /* homepage */ QStringLiteral("https://www.homepage.com/"), /* category */ QStringLiteral("CategoryA"), @@ -101,9 +101,9 @@ void checkName() { const ProviderConfigurationInfo providerEmptyLongname({ - /* short name */ QStringLiteral("Provider 1"), - /* long name */ QString(QLatin1String("")), - /* long description */ QStringLiteral("Provider description long"), + /* short name */ LanguageString(QStringLiteral("Provider 1")), + /* long name */ LanguageString(QString(QLatin1String(""))), + /* long description */ LanguageString(QStringLiteral("Provider description long")), /* address */ QStringLiteral("https://www.homepage.com/form/"), /* homepage */ QStringLiteral("https://www.homepage.com/"), /* category */ QStringLiteral("CategoryA"), @@ -118,9 +118,9 @@ QCOMPARE(providerEmptyLongname.getLongName().toString(), QString()); const ProviderConfigurationInfo providerWithoutShortname({ - /* short name */ QString(), - /* long name */ QStringLiteral("Provider 1"), - /* long description */ QStringLiteral("Provider description long"), + /* short name */ LanguageString(), + /* long name */ LanguageString(QStringLiteral("Provider 1")), + /* long description */ LanguageString(QStringLiteral("Provider description long")), /* address */ QStringLiteral("https://www.homepage.com/form/"), /* homepage */ QStringLiteral("https://www.homepage.com/"), /* category */ QStringLiteral("CategoryA"), @@ -183,8 +183,8 @@ QFETCH(double, mobileCentsPerMinute); QFETCH(double, mobileCentsPerCall); const ProviderConfigurationInfo provider({ - QString(), QString(), QString(), QString(), QString(), ""_L1, phone, - QString(), QString(), QString(), QString(), {} + LanguageString(), LanguageString(), LanguageString(), QString(), QString(), + ""_L1, phone, QString(), QString(), QString(), QString(), {} }); const CallCost& callCost = Env::getSingleton()->getCallCost(provider); @@ -227,9 +227,9 @@ void testProvidersAreEqual() { const ProviderConfigurationInfo provider1({ - /* short name */ QStringLiteral("Provider"), - /* long name */ QStringLiteral("Provider - long name"), - /* long description */ QStringLiteral("Provider description long"), + /* short name */ LanguageString(QStringLiteral("Provider")), + /* long name */ LanguageString(QStringLiteral("Provider - long name")), + /* long description */ LanguageString(QStringLiteral("Provider description long")), /* address */ QStringLiteral("https://www.homepage.com/form/"), /* homepage */ QStringLiteral("https://www.homepage.com/"), /* category */ QStringLiteral("CategoryA"), @@ -242,9 +242,9 @@ }); const ProviderConfigurationInfo provider2({ - /* short name */ QStringLiteral("Provider"), - /* long name */ QStringLiteral("Provider - long name"), - /* long description */ QStringLiteral("Provider description long"), + /* short name */ LanguageString(QStringLiteral("Provider")), + /* long name */ LanguageString(QStringLiteral("Provider - long name")), + /* long description */ LanguageString(QStringLiteral("Provider description long")), /* address */ QStringLiteral("https://www.homepage.com/form/"), /* homepage */ QStringLiteral("https://www.homepage.com/"), /* category */ QStringLiteral("CategoryB"), @@ -257,9 +257,9 @@ }); const ProviderConfigurationInfo provider3({ - /* short name */ QStringLiteral("Provider"), - /* long name */ QStringLiteral("Provider - long name"), - /* long description */ QStringLiteral("Provider description long"), + /* short name */ LanguageString(QStringLiteral("Provider")), + /* long name */ LanguageString(QStringLiteral("Provider - long name")), + /* long description */ LanguageString(QStringLiteral("Provider description long")), /* address */ QStringLiteral("https://www.homepage.com/form/"), /* homepage */ QStringLiteral("https://www.homepage.com/"), /* category */ QStringLiteral("CategoryB"), diff -Nru ausweisapp2-2.3.1/test/qt/configuration/test_ProviderConfigurationParser.cpp ausweisapp2-2.4.0/test/qt/configuration/test_ProviderConfigurationParser.cpp --- ausweisapp2-2.3.1/test/qt/configuration/test_ProviderConfigurationParser.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/configuration/test_ProviderConfigurationParser.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -53,8 +53,8 @@ " {" " \"longName\": {\"\" : \"Selbstauskunft - \\\"Meine Daten einsehen\\\"\"}," " \"shortName\": {\"\" : \"Selbstauskunft\"}," - " \"shortDescription\": {\"\" : \"Funktion der AusweisApp2\"}," - " \"longDescription\": {\"\" : \"Die AusweisApp2 verfuegt ueber die Funktion \\\"Meine Daten einsehen\\\"\"}," + " \"shortDescription\": {\"\" : \"Selbstauskunft der AusweisAp\"}," + " \"longDescription\": {\"\" : \"Die AusweisApp verfuegt ueber die Selbstauskunft \\\"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\"," @@ -65,8 +65,8 @@ " {" " \"longName\": {\"\" : \"Selbstauskunft\"}," " \"shortName\": {\"\" : \"\"}," - " \"shortDescription\": {\"\" : \"Funktion der AusweisApp2\"}," - " \"longDescription\": {\"\" : \"Die AusweisApp2 verfuegt ueber die Funktion \\\"Meine Daten einsehen\\\"\"}," + " \"shortDescription\": {\"\" : \"Selbstauskunft der AusweisApp\"}," + " \"longDescription\": {\"\" : \"Die AusweisApp verfuegt ueber die Selbstauskunft \\\"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\"," @@ -111,7 +111,7 @@ provider = providers[1]; QCOMPARE(provider.getShortName().toString(), QStringLiteral("Selbstauskunft")); QCOMPARE(provider.getLongName().toString(), QStringLiteral("Selbstauskunft - \"Meine Daten einsehen\"")); - QCOMPARE(provider.getLongDescription().toString(), QStringLiteral("Die AusweisApp2 verfuegt ueber die Funktion \"Meine Daten einsehen\"")); + QCOMPARE(provider.getLongDescription().toString(), QStringLiteral("Die AusweisApp verfuegt ueber die Selbstauskunft \"Meine Daten einsehen\"")); QCOMPARE(provider.getAddress(), QStringLiteral("https://www.ausweisapp.bund.de/online-ausweisen/meine-daten-auslesen/")); QCOMPARE(provider.getHomepage(), QStringLiteral("https://www.ausweisapp.bund.de/")); QCOMPARE(provider.getPhone(), QStringLiteral("+49 421 - 204 95 995")); @@ -185,8 +185,8 @@ QTest::addColumn("majorVersion"); QTest::addColumn("count"); - const int all = 132; - const int withEidSupport = 110; + const int all = 133; + const int withEidSupport = 112; QTest::newRow("win") << QOperatingSystemVersion::Windows << -1 << all; QTest::newRow("mac") << QOperatingSystemVersion::MacOS << -1 << all; QTest::newRow("linux") << QOperatingSystemVersion::Unknown << -1 << all; diff -Nru ausweisapp2-2.3.1/test/qt/configuration/test_ReaderConfiguration.cpp ausweisapp2-2.4.0/test/qt/configuration/test_ReaderConfiguration.cpp --- ausweisapp2-2.3.1/test/qt/configuration/test_ReaderConfiguration.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/configuration/test_ReaderConfiguration.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -97,7 +97,7 @@ QTest::addColumn("readerIcon"); QTest::addColumn("readerPattern"); - QTest::newRow("Remote card reader") << 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.*"; // codespell:ignore QTest::newRow("PersoSim SaC") << UsbId(0x0000, 0x0001) << "PersoSimHandshake 00 00" << QStringLiteral("PersoSim") << "img_PersoSim" << "^PersoSim.*"; QTest::newRow("PersoSim native") << UsbId(0x0000, 0x0001) << "PersoSim Virtual Card Reader 0" << QStringLiteral("PersoSim") << "img_PersoSim" << "^PersoSim.*"; @@ -169,7 +169,7 @@ QTest::newRow("UU") << UsbId(0xFFFF, 0xFFFF) << "crap" << "crap"; - QTest::newRow("Remote card reader") << UsbId(0x0000, 0x0000) << "NFC-abcdef1234567890" << QStringLiteral("Smartphone als Kartenleser"); + QTest::newRow("Remote card reader") << UsbId(0x0000, 0x0000) << "NFC-abcdef1234567890" << QStringLiteral("Smartphone als Kartenleser"); // codespell:ignore QTest::newRow("PersoSim SaC") << UsbId(0x0000, 0x0001) << "PersoSimHandshake 00 00" << QStringLiteral("PersoSim"); QTest::newRow("PersoSim native") << UsbId(0x0000, 0x0001) << "PersoSim Virtual Card Reader 0" << QStringLiteral("PersoSim"); diff -Nru ausweisapp2-2.3.1/test/qt/configuration/test_ReleaseInformation.cpp ausweisapp2-2.4.0/test/qt/configuration/test_ReleaseInformation.cpp --- ausweisapp2-2.3.1/test/qt/configuration/test_ReleaseInformation.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/configuration/test_ReleaseInformation.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -28,6 +28,14 @@ } + void test_betaVersionUpdate() + { + QCoreApplication::setApplicationVersion("1.2.100"_L1); + ReleaseInformation information; + QCOMPARE(information.requiresInitialUpdate(), true); + } + + void test_versionNumber() { ReleaseInformation information; diff -Nru ausweisapp2-2.3.1/test/qt/core/test_AppController.cpp ausweisapp2-2.4.0/test/qt/core/test_AppController.cpp --- ausweisapp2-2.3.1/test/qt/core/test_AppController.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/core/test_AppController.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -64,14 +64,14 @@ }); QSignalSpy spy(mController.data(), &AppController::fireWorkflowStarted); - QTest::ignoreMessage(QtInfoMsg, "Started new workflow PIN"); + QTest::ignoreMessage(QtInfoMsg, "Started new workflow CHANGE_PIN"); QTest::ignoreMessage(QtDebugMsg, "Start governikus::ChangePinController"); QVERIFY(mController->startNewWorkflow(ChangePinController::createWorkflowRequest())); QVERIFY(mController->mActiveWorkflow->getController()->mContext->wasClaimed()); - QCOMPARE(mController->mActiveWorkflow->getController()->mContext->getAction(), Action::PIN); + QCOMPARE(mController->mActiveWorkflow->getController()->mContext->getAction(), Action::CHANGE_PIN); QCOMPARE(spy.count(), 1); - QTest::ignoreMessage(QtWarningMsg, "Cannot start new workflow: PIN | Current workflow: PIN"); + QTest::ignoreMessage(QtWarningMsg, "Cannot start new workflow: CHANGE_PIN | Current workflow: CHANGE_PIN"); QVERIFY(!mController->startNewWorkflow(ChangePinController::createWorkflowRequest())); } @@ -107,14 +107,14 @@ mController->onWorkflowRequested(ChangePinController::createWorkflowRequest()); mController->onWorkflowRequested(ChangePinController::createWorkflowRequest()); QTest::ignoreMessage(QtDebugMsg, "governikus::ChangePinController done"); - QTest::ignoreMessage(QtInfoMsg, "Finish workflow PIN"); + QTest::ignoreMessage(QtInfoMsg, "Finish workflow CHANGE_PIN"); QTest::ignoreMessage(QtDebugMsg, "Running waiting action now."); - QTest::ignoreMessage(QtInfoMsg, "Started new workflow PIN"); + QTest::ignoreMessage(QtInfoMsg, "Started new workflow CHANGE_PIN"); mController->onWorkflowFinished(); QCOMPARE(spyWorkflowFinished.count(), 1); QVERIFY(!mController->mWaitingRequest); QVERIFY(mController->mActiveWorkflow); - QCOMPARE(mController->mActiveWorkflow->getController()->mContext->getAction(), Action::PIN); + QCOMPARE(mController->mActiveWorkflow->getController()->mContext->getAction(), Action::CHANGE_PIN); } @@ -126,7 +126,7 @@ mController->mActiveWorkflow->getController()->mContext->setWorkflowFinished(true); mController->onWorkflowRequested(AuthController::createWorkflowRequest(QUrl(QStringLiteral("https://localhost")))); QTest::ignoreMessage(QtDebugMsg, "governikus::SelfAuthController done"); - QTest::ignoreMessage(QtInfoMsg, "Finish workflow SELF"); + QTest::ignoreMessage(QtInfoMsg, "Finish workflow SELF_AUTH"); QTest::ignoreMessage(QtDebugMsg, "Running waiting action now."); QTest::ignoreMessage(QtInfoMsg, "Started new workflow AUTH"); mController->onWorkflowFinished(); @@ -159,9 +159,9 @@ pRequest->getContext()->claim(this); }); - QTest::ignoreMessage(QtDebugMsg, "New workflow requested: SELF"); + QTest::ignoreMessage(QtDebugMsg, "New workflow requested: SELF_AUTH"); QTest::ignoreMessage(QtDebugMsg, "Start governikus::SelfAuthController"); - QTest::ignoreMessage(QtInfoMsg, "Started new workflow SELF"); + QTest::ignoreMessage(QtInfoMsg, "Started new workflow SELF_AUTH"); mController->onWorkflowRequested(SelfAuthController::createWorkflowRequest()); QVERIFY(mController->mActiveWorkflow->getController()->mContext->wasClaimed()); mController->mActiveWorkflow->getController()->mContext->setWorkflowFinished(true); @@ -173,12 +173,12 @@ mController->onWorkflowRequested(AuthController::createWorkflowRequest(QUrl())); QVERIFY(mController->mActiveWorkflow->getController()->mContext->isStateApproved()); - QTest::ignoreMessage(QtDebugMsg, "New workflow requested: SELF"); - QTest::ignoreMessage(QtWarningMsg, "Cannot start or enqueue workflow: SELF"); + QTest::ignoreMessage(QtDebugMsg, "New workflow requested: SELF_AUTH"); + QTest::ignoreMessage(QtWarningMsg, "Cannot start or enqueue workflow: SELF_AUTH"); mController->onWorkflowRequested(SelfAuthController::createWorkflowRequest()); QTest::ignoreMessage(QtDebugMsg, "New workflow requested: AUTH"); - QTest::ignoreMessage(QtWarningMsg, "Skip workflow: AUTH | Current workflow: SELF"); + QTest::ignoreMessage(QtWarningMsg, "Skip workflow: AUTH | Current workflow: SELF_AUTH"); QTest::ignoreMessage(QtDebugMsg, "Waiting workflow: AUTH"); mController->onWorkflowRequested(AuthController::createWorkflowRequest(QUrl())); } @@ -186,9 +186,9 @@ void test_notClaimed() { - QTest::ignoreMessage(QtDebugMsg, "New workflow requested: SELF"); + QTest::ignoreMessage(QtDebugMsg, "New workflow requested: SELF_AUTH"); QTest::ignoreMessage(QtDebugMsg, "Start governikus::SelfAuthController"); - QTest::ignoreMessage(QtInfoMsg, "Started new workflow SELF"); + QTest::ignoreMessage(QtInfoMsg, "Started new workflow SELF_AUTH"); QTest::ignoreMessage(QtCriticalMsg, "Workflow was not claimed by any UI... aborting"); QTest::ignoreMessage(QtWarningMsg, "Killing the current workflow."); diff -Nru ausweisapp2-2.3.1/test/qt/crypto/test_Randomizer.cpp ausweisapp2-2.4.0/test/qt/crypto/test_Randomizer.cpp --- ausweisapp2-2.3.1/test/qt/crypto/test_Randomizer.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/crypto/test_Randomizer.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,9 +4,8 @@ #include "Randomizer.h" -#include +#include #include -#include using namespace governikus; @@ -16,35 +15,6 @@ Q_OBJECT private Q_SLOTS: - void defaultSeed() - { - QVERIFY(Randomizer::getInstance().getGenerator().default_seed == 5489u); - } - - - void secureSeeded() - { - Randomizer& randomizer = Randomizer::getInstance(); - QVERIFY(randomizer.isSecureRandom()); - } - - - void defaultSeedNotUsed() - { - std::mt19937 unseeded; - std::mt19937_64 unseeded64; - - auto& randomizer = Randomizer::getInstance(); - - for (int i = 0; i < 10; ++i) - { - const auto fromRandomizer = randomizer.getGenerator()(); - QVERIFY(fromRandomizer != unseeded()); - QVERIFY(fromRandomizer != unseeded64()); - } - } - - void createBytes() { auto& randomizer = Randomizer::getInstance(); @@ -65,23 +35,7 @@ const auto& uuid2 = randomizer.createUuid(); QVERIFY(!uuid2.isNull()); - QVERIFY(uuid1 != uuid2); - } - - - void universalBuffer() - { - Randomizer::UniversalBuffer buffer; - QCOMPARE(buffer.get(), 0); - - buffer.set(666); - QCOMPARE(buffer.get(), 666); - - memcpy(buffer.data, "1234567", sizeof(buffer.data)); - QCOMPARE(buffer.get(), 15540725856023089); - - buffer.set(13847469359445559); - QCOMPARE(QLatin1String(reinterpret_cast(buffer.data)), QLatin1String("7654321")); + QCOMPARE_NE(uuid1, uuid2); } diff -Nru ausweisapp2-2.3.1/test/qt/diagnosis/test_DiagnosisAntivirusDetection.cpp ausweisapp2-2.4.0/test/qt/diagnosis/test_DiagnosisAntivirusDetection.cpp --- ausweisapp2-2.3.1/test/qt/diagnosis/test_DiagnosisAntivirusDetection.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/diagnosis/test_DiagnosisAntivirusDetection.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -49,10 +49,10 @@ void test_parsingNullAntivirusInformation() { QSignalSpy spy(mAntivirusDetection.data(), &DiagnosisAntivirusDetection::fireAntivirusInformationChanged); - QVERIFY(mAntivirusDetection->getAntivirusInformations().empty()); + QVERIFY(mAntivirusDetection->getAntivirusInformation().empty()); QCOMPARE(spy.count(), 0); mAntivirusDetection->parseAntivirInfos(QString()); - QVERIFY(mAntivirusDetection->getAntivirusInformations().empty()); + QVERIFY(mAntivirusDetection->getAntivirusInformation().empty()); QCOMPARE(spy.count(), 1); } @@ -60,12 +60,12 @@ void test_parsingOneAntivirusInformation() { QSignalSpy spy(mAntivirusDetection.data(), &DiagnosisAntivirusDetection::fireAntivirusInformationChanged); - QVERIFY(mAntivirusDetection->getAntivirusInformations().empty()); + QVERIFY(mAntivirusDetection->getAntivirusInformation().empty()); QCOMPARE(spy.count(), 0); const QString& fileContent = getTestData(QStringLiteral("antivir_one_antivirus.txt")); mAntivirusDetection->parseAntivirInfos(fileContent); - auto antivirusInfos = mAntivirusDetection->getAntivirusInformations(); + auto antivirusInfos = mAntivirusDetection->getAntivirusInformation(); QVERIFY(!antivirusInfos.empty()); QCOMPARE(antivirusInfos[0]->getDisplayName(), "Windows Defender"_L1); QCOMPARE(antivirusInfos[0]->getLastUpdate(), "Mon, 26 Nov 2018 10:34:23 GMT"_L1); @@ -77,12 +77,12 @@ void test_parsingOneAntivirusInformationMissingTimestamp() { QSignalSpy spy(mAntivirusDetection.data(), &DiagnosisAntivirusDetection::fireAntivirusInformationChanged); - QVERIFY(mAntivirusDetection->getAntivirusInformations().empty()); + QVERIFY(mAntivirusDetection->getAntivirusInformation().empty()); QCOMPARE(spy.count(), 0); const QString& fileContent = getTestData(QStringLiteral("antivir_one_antivirus_missing_timestamp.txt")); mAntivirusDetection->parseAntivirInfos(fileContent); - auto antivirusInfos = mAntivirusDetection->getAntivirusInformations(); + auto antivirusInfos = mAntivirusDetection->getAntivirusInformation(); QVERIFY(!antivirusInfos.empty()); QCOMPARE(antivirusInfos[0]->getDisplayName(), "Windows Defender"_L1); QCOMPARE(antivirusInfos[0]->getLastUpdate(), QString()); @@ -94,12 +94,12 @@ void test_parsingTwoAntivirusInformation() { QSignalSpy spy(mAntivirusDetection.data(), &DiagnosisAntivirusDetection::fireAntivirusInformationChanged); - QVERIFY(mAntivirusDetection->getAntivirusInformations().empty()); + QVERIFY(mAntivirusDetection->getAntivirusInformation().empty()); QCOMPARE(spy.count(), 0); const QString& fileContent = getTestData(QStringLiteral("antivir_two_antivirus.txt")); mAntivirusDetection->parseAntivirInfos(fileContent); - auto antivirusInfos = mAntivirusDetection->getAntivirusInformations(); + auto antivirusInfos = mAntivirusDetection->getAntivirusInformation(); QVERIFY(!antivirusInfos.empty()); QCOMPARE(antivirusInfos[0]->getDisplayName(), "BullGuard Antivirus"_L1); QCOMPARE(antivirusInfos[0]->getLastUpdate(), "Fri, 30 Nov 2018 15:04:13 GMT"_L1); @@ -114,12 +114,12 @@ void test_parsingTwoBrokenAntivirusInformation() { QSignalSpy spy(mAntivirusDetection.data(), &DiagnosisAntivirusDetection::fireAntivirusInformationChanged); - QVERIFY(mAntivirusDetection->getAntivirusInformations().empty()); + QVERIFY(mAntivirusDetection->getAntivirusInformation().empty()); QCOMPARE(spy.count(), 0); const QString& fileContent = getTestData(QStringLiteral("antivir_two_broken_antivirus.txt")); mAntivirusDetection->parseAntivirInfos(fileContent); - auto antivirusInfos = mAntivirusDetection->getAntivirusInformations(); + auto antivirusInfos = mAntivirusDetection->getAntivirusInformation(); QVERIFY(antivirusInfos.empty()); QCOMPARE(spy.count(), 1); } diff -Nru ausweisapp2-2.3.1/test/qt/diagnosis/test_DiagnosisFirewallDetection.cpp ausweisapp2-2.4.0/test/qt/diagnosis/test_DiagnosisFirewallDetection.cpp --- ausweisapp2-2.3.1/test/qt/diagnosis/test_DiagnosisFirewallDetection.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/diagnosis/test_DiagnosisFirewallDetection.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -137,7 +137,7 @@ QTest::newRow("disabled") << QStringLiteral("firewall_profiles_disabled.txt") << false << false << false << 3; QTest::newRow("domain_disabled") << QStringLiteral("firewall_profiles_domain_disabled.txt") << false << true << true << 3; QTest::newRow("domain_private_disabled") << QStringLiteral("firewall_profiles_domain_private_disabled.txt") << false << false << true << 3; - QTest::newRow("domain_public_disbaled") << QStringLiteral("firewall_profiles_domain_public_disabled.txt") << false << true << false << 3; + QTest::newRow("domain_public_disabled") << QStringLiteral("firewall_profiles_domain_public_disabled.txt") << false << true << false << 3; QTest::newRow("private_disabled") << QStringLiteral("firewall_profiles_private_disabled.txt") << true << false << true << 3; QTest::newRow("private_public_disabled") << QStringLiteral("firewall_profiles_private_public_disabled.txt") << true << false << false << 3; QTest::newRow("public_disabled") << QStringLiteral("firewall_profiles_public_disabled.txt") << true << true << false << 3; diff -Nru ausweisapp2-2.3.1/test/qt/diagnosis/test_DiagnosisModel.cpp ausweisapp2-2.4.0/test/qt/diagnosis/test_DiagnosisModel.cpp --- ausweisapp2-2.3.1/test/qt/diagnosis/test_DiagnosisModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/diagnosis/test_DiagnosisModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -66,7 +66,7 @@ void test_newDiagnosisModel() { - QCOMPARE(mModel->rowCount(), mModel->mSections.size()); + QCOMPARE(mModel->rowCount(QModelIndex()), mModel->mSections.size()); QCOMPARE(mModel->getSectionName(DiagnosisModel::Section::GENERAL), QCoreApplication::applicationName()); QCOMPARE(mModel->getSectionName(DiagnosisModel::Section::READER), "Card reader"_L1); @@ -310,7 +310,7 @@ void test_SavePlainText() { - static const QRegularExpression re(QLatin1StringView(R"(^Test_diagnosis_DiagnosisModel\r? + static const QRegularExpression re(QStringLiteral(R"(^Test_diagnosis_DiagnosisModel\r? Application\r? Test_diagnosis_DiagnosisModel\r? Application Version\r? diff -Nru ausweisapp2-2.3.1/test/qt/global/test_CardReturnCode.cpp ausweisapp2-2.4.0/test/qt/global/test_CardReturnCode.cpp --- ausweisapp2-2.3.1/test/qt/global/test_CardReturnCode.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/global/test_CardReturnCode.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,7 +5,6 @@ #include "CardReturnCode.h" #include "ECardApiResult.h" -#include #include #include diff -Nru ausweisapp2-2.3.1/test/qt/global/test_ECardApiResult.cpp ausweisapp2-2.4.0/test/qt/global/test_ECardApiResult.cpp --- ausweisapp2-2.3.1/test/qt/global/test_ECardApiResult.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/global/test_ECardApiResult.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -357,7 +357,7 @@ QTest::newRow("invalidKey") << ECardApiResult::Minor::SAL_Invalid_Key << tr("This action cannot be performed. The eID function of your ID card is not activated."); QTest::newRow("securityConditionNotSatisfied") << ECardApiResult::Minor::SAL_SecurityConditionNotSatisfied << 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."); QTest::newRow("ageVerificationFailed") << ECardApiResult::Minor::SAL_MEAC_AgeVerificationFailedWarning << tr("The age verification failed."); - QTest::newRow("comunityVerificationFailed") << ECardApiResult::Minor::SAL_MEAC_CommunityVerificationFailedWarning << tr("The community verification failed."); + QTest::newRow("communityVerificationFailed") << ECardApiResult::Minor::SAL_MEAC_CommunityVerificationFailedWarning << tr("The community verification failed."); QTest::newRow("documentValidityVerificationFailed") << ECardApiResult::Minor::SAL_MEAC_DocumentValidityVerificationFailed << tr("The ID card is invalid or disabled."); QTest::newRow("null") << ECardApiResult::Minor::null << QString(); QTest::newRow("default") << ECardApiResult::Minor::IFDL_IFD_SharingViolation << QString(); diff -Nru ausweisapp2-2.3.1/test/qt/global/test_EnumHelper.cpp ausweisapp2-2.4.0/test/qt/global/test_EnumHelper.cpp --- ausweisapp2-2.3.1/test/qt/global/test_EnumHelper.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/global/test_EnumHelper.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -39,7 +39,7 @@ Q_OBJECT private: - void testBadConverion(const int pValue, const QString& pExpectedOutput) + void testBadConversion(const int pValue, const QString& pExpectedOutput) { QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); @@ -82,7 +82,7 @@ QString str2 = "other text"_L1 + TestEnum1::FIRST + str + TestEnum1::SECOND + " test"_L1; QCOMPARE(str, QStringLiteral("this is a dummy: FIRST")); - QCOMPARE(str2, QStringLiteral("other textFIRSTthis is a dummy: FIRSTSECOND test")); + QCOMPARE(str2, QStringLiteral("other textFIRSTthis is a dummy: FIRSTSECOND test")); // codespell:ignore } @@ -111,10 +111,10 @@ QCOMPARE(Enum::getName(TestEnum1::SECOND), QStringLiteral("SECOND")); QCOMPARE(Enum::getName(TestEnum1::THIRD), QStringLiteral("THIRD")); - testBadConverion(6, QStringLiteral("UNKNOWN 0x6") + lineBreak); - testBadConverion(255, QStringLiteral("UNKNOWN 0xff") + lineBreak); - testBadConverion(365, QStringLiteral("UNKNOWN 0x16d") + lineBreak); - testBadConverion(2147483647, QStringLiteral("UNKNOWN 0x7fffffff") + lineBreak); + testBadConversion(6, QStringLiteral("UNKNOWN 0x6") + lineBreak); + testBadConversion(255, QStringLiteral("UNKNOWN 0xff") + lineBreak); + testBadConversion(365, QStringLiteral("UNKNOWN 0x16d") + lineBreak); + testBadConversion(2147483647, QStringLiteral("UNKNOWN 0x7fffffff") + lineBreak); QCOMPARE(getEnumName(TestEnum1::SECOND), QStringLiteral("SECOND")); QCOMPARE(getEnumName(EnumTestEnum1::TestEnum1::SECOND), QStringLiteral("SECOND")); diff -Nru ausweisapp2-2.3.1/test/qt/global/test_GlobalStatus.cpp ausweisapp2-2.4.0/test/qt/global/test_GlobalStatus.cpp --- ausweisapp2-2.3.1/test/qt/global/test_GlobalStatus.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/global/test_GlobalStatus.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,6 +6,7 @@ #include + using namespace governikus; @@ -44,7 +45,7 @@ QTest::addColumn("message"); QTest::newRow("inProgress") << GlobalStatus::Code::Workflow_AlreadyInProgress_Error << tr("Cannot start authentication. An operation is already active."); - QTest::newRow("cardRemoved") << GlobalStatus::Code::Workflow_Card_Removed << tr("The connection to the ID card has been lost. The process was aborted."); + QTest::newRow("cardRemoved") << GlobalStatus::Code::Workflow_Card_Removed << tr("Restart the authentication process and make sure that the position of the ID card does not change during the reading process."); 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("preverificationDevelopermode") << GlobalStatus::Code::Workflow_Preverification_Developermode_Error << tr("Using the developer mode is only allowed in a test environment."); @@ -60,7 +61,6 @@ QTest::newRow("abnormalClose") << GlobalStatus::Code::RemoteReader_CloseCode_AbnormalClose << tr("The smartphone as card reader (SaC) connection was aborted."); QTest::newRow("invalidRequest") << GlobalStatus::Code::IfdConnector_InvalidRequest << tr("Smartphone as card reader (SaC) connection request was invalid."); QTest::newRow("noSupportedApiLevel") << GlobalStatus::Code::IfdConnector_NoSupportedApiLevel << tr("Your smartphone as card reader (SaC) version is incompatible with the local version. Please install the latest %1 version on both your smartphone and your computer.").arg(QCoreApplication::applicationName()); - QTest::newRow("connectionTimeout") << GlobalStatus::Code::IfdConnector_ConnectionTimeout << tr("A timeout occurred while trying to establish a connection to the smartphone as card reader (SaC)."); QTest::newRow("connectionError") << GlobalStatus::Code::IfdConnector_ConnectionError << tr("An error occurred while trying to establish a connection to the smartphone as card reader (SaC)."); QTest::newRow("hostRefused") << GlobalStatus::Code::IfdConnector_RemoteHostRefusedConnection << tr("The smartphone to be paired has rejected the connection. Please check the pairing code."); QTest::newRow("fileNotFound") << GlobalStatus::Code::Downloader_File_Not_Found << tr("File not found."); diff -Nru ausweisapp2-2.3.1/test/qt/global/test_LogHandler.cpp ausweisapp2-2.4.0/test/qt/global/test_LogHandler.cpp --- ausweisapp2-2.3.1/test/qt/global/test_LogHandler.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/global/test_LogHandler.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -287,7 +287,7 @@ fakeLastModifiedAndLastAccessTime(tmp.fileName()); logger->removeOldLogFiles(); QCOMPARE(logger->getOtherLogFiles().size(), initialFiles.size()); - QVERIFY(!tmp.exists()); + QVERIFY(!QFile::exists(tmp.fileName())); } @@ -371,8 +371,8 @@ fakeLastModifiedAndLastAccessTime(tmp2.fileName()); logger->init(); QTRY_COMPARE(logger->getOtherLogFiles().size(), initialFiles.size()); // clazy:exclude=qstring-allocations - QVERIFY(!tmp1.exists()); - QVERIFY(!tmp2.exists()); + QVERIFY(!QFile::exists(tmp1.fileName())); + QVERIFY(!QFile::exists(tmp2.fileName())); } diff -Nru ausweisapp2-2.3.1/test/qt/global/test_VersionInfo.cpp ausweisapp2-2.4.0/test/qt/global/test_VersionInfo.cpp --- ausweisapp2-2.3.1/test/qt/global/test_VersionInfo.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/global/test_VersionInfo.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -60,11 +60,11 @@ void fromTextInvalidData() { - auto versionInfo = VersionInfo::fromText(QLatin1StringView("skdkfsn sdnk fskdfn sdfk sflk nd\n" - "\n" - ":\n" - " :\r\t\n" - "")); + auto versionInfo = VersionInfo::fromText(QStringLiteral("skdkfsn sdnk fskdfn sdfk sflk gnd\n" + "\n" + ":\n" + " :\r\t\n" + "")); QVERIFY(versionInfo.getName().isNull()); QVERIFY(versionInfo.getSpecificationTitle().isNull()); @@ -78,13 +78,13 @@ void fromText() { - auto versionInfo = VersionInfo::fromText(QLatin1StringView("Name: MyName\n" - "Specification-Title: MySpecTitle\n" - "Specification-Version: MySpecVersion\n" - "Specification-Vendor: MySpecVendor\n" - "Implementation-Title: MyImplTitle\n" - "Implementation-Version: MyImplVersion\n" - "Implementation-Vendor: MyImplVendor")); + auto versionInfo = VersionInfo::fromText(QStringLiteral("Name: MyName\n" + "Specification-Title: MySpecTitle\n" + "Specification-Version: MySpecVersion\n" + "Specification-Vendor: MySpecVendor\n" + "Implementation-Title: MyImplTitle\n" + "Implementation-Version: MyImplVersion\n" + "Implementation-Vendor: MyImplVendor")); QCOMPARE(versionInfo.getName(), QLatin1String("MyName")); QCOMPARE(versionInfo.getSpecificationTitle(), QLatin1String("MySpecTitle")); diff -Nru ausweisapp2-2.3.1/test/qt/ifd/messages/test_Discovery.cpp ausweisapp2-2.4.0/test/qt/ifd/messages/test_Discovery.cpp --- ausweisapp2-2.3.1/test/qt/ifd/messages/test_Discovery.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/messages/test_Discovery.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,11 +5,13 @@ #include "messages/Discovery.h" #include "LogHandler.h" +#include "PortFile.h" #include "TestFileHelper.h" #include +using namespace Qt::Literals::StringLiterals; using namespace governikus; @@ -40,11 +42,12 @@ QVERIFY(msg.isIncomplete()); QCOMPARE(msg.getType(), IfdMessageType::UNDEFINED); QCOMPARE(msg.getContextHandle(), QString()); + QVERIFY(!msg.isSupported()); QCOMPARE(msg.getIfdName(), QString()); QCOMPARE(msg.getIfdId(), QByteArray()); QVERIFY(msg.getPort() == 0); QCOMPARE(msg.getSupportedApis(), QList()); - QCOMPARE(msg.getPairing(), false); + QCOMPARE(msg.isPairing(), false); QCOMPARE(logSpy.count(), 6); QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Missing value \"msg\""))); @@ -64,32 +67,67 @@ 24728, {IfdVersion::Version::v0, IfdVersion::Version::v2} ); + discovery.setAddresses({QHostAddress("192.168.1.42"_L1)}); QVERIFY(!discovery.isIncomplete()); QCOMPARE(discovery.getType(), IfdMessageType::UNDEFINED); QCOMPARE(discovery.getContextHandle(), QString()); + QVERIFY(discovery.isSupported()); QCOMPARE(discovery.getIfdName(), QStringLiteral("Sony Xperia Z5 compact")); QCOMPARE(discovery.getIfdId(), QByteArrayLiteral("0123456789ABCDEF")); QVERIFY(discovery.getPort() == static_cast(24728)); QCOMPARE(discovery.getSupportedApis(), QList({IfdVersion::Version::v0, IfdVersion::Version::v2})); - QVERIFY(!discovery.getPairing()); + QVERIFY(!discovery.isPairing()); + QCOMPARE(discovery.getAddresses(), {QUrl("wss://192.168.1.42:24728"_L1)}); discovery.setPairing(true); - QVERIFY(discovery.getPairing()); + QVERIFY(discovery.isPairing()); } void toJson_data() { + QSet ipv4({QHostAddress("192.168.1.42"_L1)}); + QSet ipv6({QHostAddress("::ffff:192.168.1.42"_L1)}); + QSet ipv46({QHostAddress("192.168.1.42"_L1), QHostAddress("::ffff:192.168.1.42"_L1)}); + + QByteArrayList ipv4_plain({"wss://192.168.1.42:24728"}); + QByteArrayList ipv6_plain({"wss://[::ffff:192.168.1.42]:24728"}); + QByteArrayList ipv46_plain({"wss://[::ffff:192.168.1.42]:24728\",\n \"wss://192.168.1.42:24728", + "wss://192.168.1.42:24728\",\n \"wss://[::ffff:192.168.1.42]:24728"}); + QTest::addColumn("version"); QTest::addColumn("pairing"); - QTest::addColumn("json"); - QTest::newRow("Unknown - Pairing enabled") << IfdVersion::Version::Unknown << true << QByteArray(); - QTest::newRow("Unknown - Pairing disabled") << IfdVersion::Version::Unknown << false << QByteArray(); - QTest::newRow("v0 - Pairing enabled") << IfdVersion::Version::v0 << true << QByteArray(); - QTest::newRow("v0 - Pairing disabled") << IfdVersion::Version::v0 << false << QByteArray(); - QTest::newRow("v2 - Pairing enabled") << IfdVersion::Version::v2 << true << QByteArray(" \"pairing\": true,\n"); - QTest::newRow("v2 - Pairing disabled") << IfdVersion::Version::v2 << false << QByteArray(" \"pairing\": false,\n"); + QTest::addColumn("json_pairing"); + QTest::addColumn>("hostAddresses"); + QTest::addColumn("json_address"); + QTest::newRow("Unknown - Pairing enabled - IPv4") << IfdVersion::Version::Unknown + << true << QByteArray() + << ipv4 << ipv4_plain; + QTest::newRow("Unknown - Pairing disabled - IPv6") << IfdVersion::Version::Unknown + << false << QByteArray() + << ipv6 << ipv6_plain; + QTest::newRow("Unknown - Pairing disabled - IPv4/6") << IfdVersion::Version::Unknown + << false << QByteArray() + << ipv46 << ipv46_plain; + QTest::newRow("v0 - Pairing enabled - IPv4") << IfdVersion::Version::v0 + << true << QByteArray() + << ipv4 << ipv4_plain; + QTest::newRow("v0 - Pairing disabled - IPv6") << IfdVersion::Version::v0 + << false << QByteArray() + << ipv6 << ipv6_plain; + QTest::newRow("v0 - Pairing disabled - IPv4/6") << IfdVersion::Version::v0 + << false << QByteArray() + << ipv46 << ipv46_plain; + QTest::newRow("v2 - Pairing enabled - IPv6") << IfdVersion::Version::v2 + << true << QByteArray(" \"pairing\": true,\n") + << ipv4 << ipv4_plain; + QTest::newRow("v2 - Pairing disabled - IPv6") << IfdVersion::Version::v2 + << false << QByteArray(" \"pairing\": false,\n") + << ipv6 << ipv6_plain; + QTest::newRow("v2 - Pairing disabled - IPv4/6") << IfdVersion::Version::v2 + << false << QByteArray(" \"pairing\": false,\n") + << ipv46 << ipv46_plain; } @@ -97,7 +135,9 @@ { QFETCH(IfdVersion::Version, version); QFETCH(bool, pairing); - QFETCH(QByteArray, json); + QFETCH(QByteArray, json_pairing); + QFETCH(QSet, hostAddresses); + QFETCH(QByteArrayList, json_address); Discovery discovery( QStringLiteral("Sony Xperia Z5 compact"), @@ -106,23 +146,34 @@ {IfdVersion::Version::v0, IfdVersion::Version::v2} ); discovery.setPairing(pairing); + discovery.setAddresses(hostAddresses); const QByteArray& byteArray = discovery.toByteArray(version); - QCOMPARE(byteArray, - QByteArray("{\n" - " \"IFDID\": \"0123456789abcdef\",\n" - " \"IFDName\": \"Sony Xperia Z5 compact\",\n" - " \"SupportedAPI\": [\n" - " \"IFDInterface_WebSocket_v0\",\n" - " \"IFDInterface_WebSocket_v2\"\n" - " ],\n" - " \"msg\": \"REMOTE_IFD\",\n" - "[PAIRING]" - " \"port\": 24728\n" - "}\n").replace("[PAIRING]", json)); + int matches = 0; + for (const auto& address : json_address) + { + if (byteArray == QByteArray("{\n" + " \"IFDID\": \"0123456789abcdef\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " \"IFDInterface_WebSocket_v0\",\n" + " \"IFDInterface_WebSocket_v2\"\n" + " ],\n" + " \"addresses\": [\n" + " \"[ADDRESS]\"\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + "[PAIRING]" + " \"port\": 24728\n" + "}\n").replace("[ADDRESS]", address).replace("[PAIRING]", json_pairing)) + { + matches++; + } + } + QCOMPARE(matches, 1); const QJsonObject obj = QJsonDocument::fromJson(byteArray).object(); - QCOMPARE(obj.size(), version >= IfdVersion::Version::v2 ? 6 : 5); + QCOMPARE(obj.size(), version >= IfdVersion::Version::v2 ? 7 : 6); QCOMPARE(obj.value(QLatin1String("IFDName")).toString(), QStringLiteral("Sony Xperia Z5 compact")); QCOMPARE(obj.value(QLatin1String("IFDID")).toString(), QStringLiteral("0123456789abcdef")); QCOMPARE(obj.value(QLatin1String("msg")).toString(), QStringLiteral("REMOTE_IFD")); @@ -133,45 +184,62 @@ QCOMPARE(apiLevels.toArray().size(), 2); QCOMPARE(apiLevels.toArray().at(0).toString(), QStringLiteral("IFDInterface_WebSocket_v0")); QCOMPARE(apiLevels.toArray().at(1).toString(), QStringLiteral("IFDInterface_WebSocket_v2")); + const QJsonValue addresses = obj.value(QLatin1String("addresses")); + QVERIFY(addresses.isArray()); + QCOMPARE(addresses.toArray().size(), hostAddresses.size()); } void fromJson_data() { - QTest::addColumn("json"); + QTest::addColumn("json_pairing"); QTest::addColumn("pairing"); - QTest::newRow("Pairing enabled") << QByteArray(R"("pairing": true,)") << true; - QTest::newRow("Pairing disabled") << QByteArray(R"("pairing": false,)") << false; + QTest::addColumn("json_address"); + QTest::addColumn>("addresses"); + QTest::newRow("Pairing enabled - IPv4") << "\"pairing\": true,"_ba << true + << "\"wss://192.168.1.42:24728\""_ba << QSet({QUrl("wss://192.168.1.42:24728"_L1)}); + QTest::newRow("Pairing disabled - IPv6") << "\"pairing\": false,"_ba << false + << "\"wss://[::ffff:192.168.1.42]:24728\""_ba << QSet({QUrl("wss://[::ffff:192.168.1.42]:24728"_L1)}); + QTest::newRow("Pairing disabled - IPv4/6") << "\"pairing\": false,"_ba << false + << "\"wss://192.168.1.42:24728\", \"wss://[::ffff:192.168.1.42]:24728\""_ba << QSet({QUrl("wss://192.168.1.42:24728"_L1), QUrl("wss://[::ffff:192.168.1.42]:24728"_L1)}); } void fromJson() { - QFETCH(QByteArray, json); + QFETCH(QByteArray, json_pairing); QFETCH(bool, pairing); + QFETCH(QByteArray, json_address); + QFETCH(QSet, addresses); QByteArray message(R"({ "IFDID": "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF", "IFDName": "Sony Xperia Z5 compact", "SupportedAPI": [ - "IFDInterface_WebSocket_v0" + "IFDInterface_WebSocket_v2" + ], + "addresses": [ + [ADDRESS] ], "msg": "REMOTE_IFD", [PAIRING] "port": 24728 })"); - message.replace("[PAIRING]", json); + message.replace("[ADDRESS]", json_address).replace("[PAIRING]", json_pairing); const QJsonObject& obj = QJsonDocument::fromJson(message).object(); const Discovery discovery(obj); QVERIFY(!discovery.isIncomplete()); QCOMPARE(discovery.getType(), IfdMessageType::UNDEFINED); QCOMPARE(discovery.getContextHandle(), QString()); + QVERIFY(discovery.isSupported()); QCOMPARE(discovery.getIfdName(), QStringLiteral("Sony Xperia Z5 compact")); QCOMPARE(discovery.getIfdId(), QByteArray::fromHex(QByteArrayLiteral("0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"))); QVERIFY(discovery.getPort() == static_cast(24728)); - QCOMPARE(discovery.getSupportedApis(), QList({IfdVersion::Version::v0})); - QCOMPARE(discovery.getPairing(), pairing); + QCOMPARE(discovery.getAddresses(), addresses); + QCOMPARE(discovery.addressesMissing(), addresses.isEmpty()); + QCOMPARE(discovery.getSupportedApis(), QList({IfdVersion::Version::v2})); + QCOMPARE(discovery.isPairing(), pairing); } @@ -225,16 +293,19 @@ QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); - QByteArray message(R"({ - "IFDID": "[IFDID]", - "IFDName": "Sony Xperia Z5 compact", - "SupportedAPI": [ - [VERSION] - ], - "msg": "REMOTE_IFD", - [PAIRING] - "port": 24728 - })"); + QByteArray message("{\n" + " \"IFDID\": \"[IFDID]\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " [VERSION]\n" + " ],\n" + " \"addresses\": [\n" + " \"wss://192.168.1.42:27728\"\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + " [PAIRING]\n" + " \"port\": 24728\n" + "}"); message.replace("[VERSION]", json_version); message.replace("[IFDID]", json_ifdid); message.replace("[PAIRING]", json_pairing); @@ -242,7 +313,7 @@ const QJsonObject& obj = QJsonDocument::fromJson(message).object(); const Discovery discovery(obj); QCOMPARE(discovery.isIncomplete(), incomplete); - QCOMPARE(discovery.getPairing(), pairing); + QCOMPARE(discovery.isPairing(), pairing); QCOMPARE(logSpy.count(), incomplete ? 1 : 0); if (incomplete) @@ -270,14 +341,15 @@ QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); - QByteArray message(R"({ - "IFDName": "Sony Xperia Z5 compact", - "IFDID": "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF", - "port": 24728, - "SupportedAPI": ["IFDInterface_WebSocket_v0"], - "pairing": true, - "msg": "%1" - })"); + QByteArray message("{\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"IFDID\": \"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\",\n" + " \"port\": 24728,\n" + " \"addresses\": [\"wss://192.168.1.42:27728\"],\n" + " \"SupportedAPI\": [\"IFDInterface_WebSocket_v0\"],\n" + " \"pairing\": true,\n" + " \"msg\": \"%1\"\n" + "}"); const QJsonObject& obj = QJsonDocument::fromJson(message.replace("%1", type)).object(); const Discovery discovery(obj); @@ -293,17 +365,20 @@ { QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); - const QByteArray message(R"({ - "ContextHandle": "TestContext", - "IFDID": "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF", - "IFDName": "Sony Xperia Z5 compact", - "SupportedAPI": [ - "IFDInterface_WebSocket_v0" - ], - "msg": "REMOTE_IFD", - "pairing": true, - "port": 24728 - })"); + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDID\": \"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " \"IFDInterface_WebSocket_v0\"\n" + " ],\n" + " \"addresses\": [\n" + " \"wss://192.168.1.42:27728\"\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"pairing\": true,\n" + " \"port\": 24728\n" + "}"); const QJsonObject& obj = QJsonDocument::fromJson(message).object(); const Discovery discovery(obj); @@ -324,24 +399,55 @@ "SupportedAPI": "IFDInterface_WebSocket_v0", "msg": "REMOTE_IFD", "pairing": 3, - "port": "4" + "port": "4", + "addresses": "192.168.1.42:24728" })"); const QJsonObject& obj = QJsonDocument::fromJson(message).object(); const Discovery discovery(obj); QVERIFY(discovery.isIncomplete()); + QVERIFY(!discovery.isSupported()); QCOMPARE(discovery.getIfdName(), QString()); QCOMPARE(discovery.getIfdId(), QByteArray()); QVERIFY(discovery.getPort() == 0); QCOMPARE(discovery.getSupportedApis(), QList()); - QCOMPARE(discovery.getPairing(), false); + QCOMPARE(discovery.isPairing(), false); - QCOMPARE(logSpy.count(), 5); + QCOMPARE(logSpy.count(), 6); QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("The value of \"IFDName\" should be of type \"string\""))); QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("The value of \"IFDID\" should be of type \"string\""))); QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("The value of \"port\" should be of type \"number\""))); QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("The value of \"SupportedAPI\" should be of type \"array\""))); QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("The value of \"pairing\" should be of type \"boolean\""))); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("The value of \"addresses\" should be of type \"array\""))); + } + + + void emptyApiArray() + { + QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDID\": \"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " ],\n" + " \"addresses\": [\n" + " \"wss://192.168.1.42:27728\"\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"pairing\": true,\n" + " \"port\": 24728\n" + "}"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const Discovery discovery(obj); + QVERIFY(discovery.isIncomplete()); + QVERIFY(discovery.getSupportedApis().isEmpty()); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("At least one entry is required for \"SupportedAPI\""))); } @@ -349,18 +455,21 @@ { QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); - const QByteArray message(R"({ - "ContextHandle": "TestContext", - "IFDID": "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF", - "IFDName": "Sony Xperia Z5 compact", - "SupportedAPI": [ - 0, - "IFDInterface_WebSocket_v0" - ], - "msg": "REMOTE_IFD", - "pairing": true, - "port": 24728 - })"); + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDID\": \"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " 0,\n" + " \"IFDInterface_WebSocket_v0\"\n" + " ],\n" + " \"addresses\": [\n" + " \"wss://192.168.1.42:27728\"\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"pairing\": true,\n" + " \"port\": 24728\n" + "}"); const QJsonObject& obj = QJsonDocument::fromJson(message).object(); const Discovery discovery(obj); @@ -372,6 +481,72 @@ } + void emptyAddresses() + { + QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDID\": \"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " \"IFDInterface_WebSocket_v0\"\n" + " ],\n" + " \"addresses\": [\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"pairing\": true,\n" + " \"port\": 24728\n" + "}"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const Discovery discovery(obj); + QVERIFY(discovery.isIncomplete()); + QVERIFY(discovery.getAddresses().isEmpty()); + + QCOMPARE(logSpy.count(), 1); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("At least one entry is required for \"addresses\""))); + } + + + void wrongAddressesType() + { + QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); + + const QByteArray message("{\n" + " \"ContextHandle\": \"TestContext\",\n" + " \"IFDID\": \"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF\",\n" + " \"IFDName\": \"Sony Xperia Z5 compact\",\n" + " \"SupportedAPI\": [\n" + " \"IFDInterface_WebSocket_v0\"\n" + " ],\n" + " \"addresses\": [\n" + " 0,\n" + " \"192.168.1.41:24728\",\n" + " \"abs://192.168.1.42:24728\",\n" + " \"wss://:24728\",\n" + " \"wss://192.168.1.42\",\n" + " \"wss://192.168.1.42:24728\"\n" + " ],\n" + " \"msg\": \"REMOTE_IFD\",\n" + " \"pairing\": true,\n" + " \"port\": 24728\n" + "}"); + + const QJsonObject& obj = QJsonDocument::fromJson(message).object(); + const Discovery discovery(obj); + QVERIFY(discovery.isIncomplete()); + QCOMPARE(discovery.getAddresses(), {QUrl("wss://192.168.1.42:24728"_L1)}); + + QCOMPARE(logSpy.count(), 5); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("The value of \"addresses\" should be of type \"string\""))); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("The value of \"addresses\" should be of type \"url\""))); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Found \"addresses\" entry with wrong scheme: abs://192.168.1.42:24728"))); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Found \"addresses\" entry without host: wss://:24728"))); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Found \"addresses\" entry without port: wss://192.168.1.42"))); + } + + void IfdId_data() { const QByteArray fingerprint("0575e99867361c26442ece18bed6f955ab7dd269ae8f42d3a21af0e734c3d8d9"); @@ -449,6 +624,76 @@ } + void setAddresses() + { + Discovery discovery( + QStringLiteral("Sony Xperia Z5 compact"), + QByteArray::fromHex(QByteArrayLiteral("0123456789ABCDEF")), + 24728, + {IfdVersion::Version::v2}); + QVERIFY(!discovery.isIncomplete()); + + discovery.setAddresses({QHostAddress()}); + QVERIFY(discovery.addressesMissing()); + + discovery.setAddresses({QHostAddress("192.168.1.10"_L1)}); + QCOMPARE(discovery.getAddresses().size(), 1); + QCOMPARE(*discovery.getAddresses().constBegin(), QUrl("wss://192.168.1.10:24728"_L1)); + + discovery.setAddresses({QHostAddress("192.168.1.10"_L1), QHostAddress("192.168.1.10"_L1)}); + QCOMPARE(discovery.getAddresses().size(), 1); + QCOMPARE(*discovery.getAddresses().constBegin(), QUrl("wss://192.168.1.10:24728"_L1)); + } + + + void setAddressesOnInvalid() + { + Discovery discovery((QJsonObject())); + QVERIFY(discovery.isIncomplete()); + + discovery.setAddresses({QHostAddress()}); + QVERIFY(discovery.getAddresses().isEmpty()); + + discovery.setAddresses({QHostAddress("192.168.1.10"_L1)}); + QVERIFY(discovery.addressesMissing()); + } + + + void test_localIfd_data() + { + QTest::addColumn("address"); + QTest::addColumn("port"); + QTest::addColumn("isLocalIfd"); + + QTest::newRow("Defined localhost IPv4") << QHostAddress(QHostAddress::LocalHost) << PortFile::cDefaultPort << true; + QTest::newRow("Defined localhost IPv4 wrong port") << QHostAddress(QHostAddress::LocalHost) << quint16(11111) << false; + QTest::newRow("Defined localhost IPv6") << QHostAddress(QHostAddress::LocalHostIPv6) << PortFile::cDefaultPort << true; + QTest::newRow("Defined localhost IPv6 wrong port") << QHostAddress(QHostAddress::LocalHostIPv6) << quint16(11111) << false; + QTest::newRow("Local Address IPv4") << QHostAddress("127.0.0.1"_L1) << PortFile::cDefaultPort << true; + QTest::newRow("Local Address IPv4 wrong port") << QHostAddress("127.0.0.1"_L1) << quint16(11111) << false; + QTest::newRow("Local Address IPv6") << QHostAddress("::1"_L1) << PortFile::cDefaultPort << true; + QTest::newRow("Local Address IPv6 wrong port") << QHostAddress("::1"_L1) << quint16(11111) << false; + QTest::newRow("Any IPv4") << QHostAddress("192.168.1.42"_L1) << PortFile::cDefaultPort << false; + QTest::newRow("Any IPv4 wrong port") << QHostAddress("192.168.1.42"_L1) << quint16(11111) << false; + QTest::newRow("Any IPv6") << QHostAddress("::ffff:192.168.1.42"_L1) << PortFile::cDefaultPort << false; + QTest::newRow("Any IPv6 wrong port") << QHostAddress("::ffff:192.168.1.42"_L1) << quint16(11111) << false; + QTest::newRow("Any name") << QHostAddress("localhost"_L1) << PortFile::cDefaultPort << false; + QTest::newRow("Any name wrong port") << QHostAddress("localhost"_L1) << quint16(11111) << false; + } + + + void test_localIfd() + { + QFETCH(QHostAddress, address); + QFETCH(quint16, port); + QFETCH(bool, isLocalIfd); + + Discovery discovery("entry 1"_L1, "01"_ba, port, {IfdVersion::supported()}); + discovery.setAddresses({address}); + QCOMPARE(discovery.isLocalIfd(), isLocalIfd); + } + + }; QTEST_GUILESS_MAIN(test_Discovery) diff -Nru ausweisapp2-2.3.1/test/qt/ifd/messages/test_IfdEstablishPaceChannelResponse.cpp ausweisapp2-2.4.0/test/qt/ifd/messages/test_IfdEstablishPaceChannelResponse.cpp --- ausweisapp2-2.3.1/test/qt/ifd/messages/test_IfdEstablishPaceChannelResponse.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/messages/test_IfdEstablishPaceChannelResponse.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -72,7 +72,8 @@ { const IfdEstablishPaceChannelResponse ifdEstablishPaceChannelResponse( QStringLiteral("SlotHandle"), - EstablishPaceChannelOutput() + EstablishPaceChannelOutput(), + ECardApiResult::Minor::null ); QVERIFY(!ifdEstablishPaceChannelResponse.isIncomplete()); @@ -105,7 +106,8 @@ const IfdEstablishPaceChannelResponse ifdEstablishPaceChannelResponse( QStringLiteral("SlotHandle"), - EstablishPaceChannelOutput() + EstablishPaceChannelOutput(), + ECardApiResult::Minor::null ); const QByteArray& byteArray = ifdEstablishPaceChannelResponse.toByteArray(version, QStringLiteral("TestContext")); @@ -384,6 +386,31 @@ } + void test_GetReturnCode_data() + { + QTest::addColumn("minor"); + QTest::addColumn("code"); + + QTest::newRow("ok") << ECardApiResult::Minor::null << CardReturnCode::OK; + QTest::newRow("timeout") << ECardApiResult::Minor::IFDL_Timeout_Error << CardReturnCode::INPUT_TIME_OUT; + QTest::newRow("cancellation") << ECardApiResult::Minor::IFDL_CancellationByUser << CardReturnCode::CANCELLATION_BY_USER; + QTest::newRow("noCard") << ECardApiResult::Minor::IFDL_Terminal_NoCard << CardReturnCode::CARD_NOT_FOUND; + QTest::newRow("invalidSlot") << ECardApiResult::Minor::IFDL_InvalidSlotHandle << CardReturnCode::CARD_NOT_FOUND; + QTest::newRow("unknownApiFunction") << ECardApiResult::Minor::AL_Unknown_API_Function << CardReturnCode::COMMAND_FAILED; + QTest::newRow("default") << ECardApiResult::Minor::AL_Unknown_API_Function << CardReturnCode::COMMAND_FAILED; + } + + + void test_GetReturnCode() + { + QFETCH(ECardApiResult::Minor, minor); + QFETCH(CardReturnCode, code); + + const IfdEstablishPaceChannelResponse response("slothandle"_L1, EstablishPaceChannelOutput(), minor); + QCOMPARE(response.getReturnCode(), code); + } + + }; QTEST_GUILESS_MAIN(test_IfdEstablishPaceChannelResponse) diff -Nru ausweisapp2-2.3.1/test/qt/ifd/remote/test_RemoteIfdClient.cpp ausweisapp2-2.4.0/test/qt/ifd/remote/test_RemoteIfdClient.cpp --- ausweisapp2-2.3.1/test/qt/ifd/remote/test_RemoteIfdClient.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/remote/test_RemoteIfdClient.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,13 +6,16 @@ #include "DatagramHandler.h" #include "Env.h" +#include "IfdListImpl.h" #include "LogHandler.h" +#include "TestFileHelper.h" #include "messages/Discovery.h" #include "messages/IfdEstablishContext.h" #include #include + using namespace Qt::Literals::StringLiterals; using namespace governikus; @@ -35,48 +38,20 @@ } - void send(const QByteArray&) override + [[nodiscard]] QList getAllBroadcastEntries() const override { + return QList(); } -}; - -DatagramHandlerMock::~DatagramHandlerMock() -{ -} - - -class IfdListMock - : public IfdList -{ - Q_OBJECT - - public: - IfdListMock(int pI1, int pI2) - : IfdList() - { - Q_UNUSED(pI1) - Q_UNUSED(pI2) - } - - - ~IfdListMock() override; - - void update(const IfdDescriptor&) override - { - Q_EMIT fireDeviceAppeared(QSharedPointer()); - } - - - void clear() override + void send(const QByteArray&, const QList&) override { } }; -IfdListMock::~IfdListMock() +DatagramHandlerMock::~DatagramHandlerMock() { } @@ -87,14 +62,14 @@ Q_OBJECT public Q_SLOTS: - void onConnectRequest(const IfdDescriptor& pRemoteDeviceDescriptor, const QByteArray& pPsk) override; + void onConnectRequest(const Discovery& pDiscovery, const QByteArray& pPsk) override; Q_SIGNALS: void fireConnectionRequestReceived(); }; -void RemoteConnectorMock::onConnectRequest(const IfdDescriptor&, const QByteArray&) +void RemoteConnectorMock::onConnectRequest(const Discovery&, const QByteArray&) { Q_EMIT fireConnectionRequestReceived(); } @@ -109,7 +84,7 @@ private: QPointer mDatagramHandlerMock; - QPointer mIfdListMock; + QPointer mIfdList; QPointer mRemoteConnectorMock; private Q_SLOTS: @@ -127,8 +102,8 @@ })); Env::setCreator(std::function([this] { - mIfdListMock = new IfdListMock(0, 0); - return mIfdListMock; + mIfdList = new IfdListImpl(5000, 5000); + return mIfdList; })); Env::setCreator(std::function([this] { @@ -143,7 +118,7 @@ QVERIFY(Env::getCounter() <= 2); Env::clear(); QVERIFY(mDatagramHandlerMock.isNull()); - QVERIFY(mIfdListMock.isNull()); + QVERIFY(mIfdList.isNull()); QVERIFY(mRemoteConnectorMock.isNull()); } @@ -170,24 +145,110 @@ } - void testReceiveUnparsable() + void testOnNewMessage_data() { - QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); - - const QByteArray unparsableJson("{\n" - " \"device___Name\": \"Sony Xperia Z5 compact\",\n" - " \"encrypted\": true,\n" - " \"port\": 24728,\n" - " \"availableApiLevels\": [1, 2, 3, 4]\n" + const QByteArray missingIFDID(R"({ + "IF__DID": "fingerprint", + "IFDName": "Sony Xperia Z5 compact", + "SupportedAPI": [ "IFDInterface_WebSocket_v2" ], + "msg": "REMOTE_IFD", + "pairing": true, + "port": 24728 + })"); + + const QByteArray emptyAddresses(R"({ + "IFDID": "fingerprint", + "IFDName": "Sony Xperia Z5 compact", + "SupportedAPI": [ "IFDInterface_WebSocket_v2" ], + "msg": "REMOTE_IFD", + "pairing": true, + "port": 24728, + "addresses": [] + })"); + + const QByteArray invalidAddresses(R"({ + "IFDID": "fingerprint", + "IFDName": "Sony Xperia Z5 compact", + "SupportedAPI": [ "IFDInterface_WebSocket_v2" ], + "msg": "REMOTE_IFD", + "pairing": true, + "port": 24728, + "addresses": [ "FooBar" ] + })"); + + const QByteArray validAddresses("{" + " \"IFDID\": \"fingerprint\"," + " \"IFDName\": \"Sony Xperia Z5 compact\"," + " \"SupportedAPI\": [ \"IFDInterface_WebSocket_v2\" ]," + " \"msg\": \"REMOTE_IFD\"," + " \"pairing\": true," + " \"port\": 24728," + " \"addresses\": [ \"wss://192.168.1.87:24728\" ]" "}"); + QTest::addColumn("discovery"); + QTest::addColumn("sender"); + QTest::addColumn>("logging"); + + QTest::newRow("Missing IFDID - No sender") + << missingIFDID << QHostAddress() + << QList({"Missing value \"IFDID\""_L1, "Discarding unparsable message"_L1}); + + QTest::newRow("Empty addresses - No sender") + << emptyAddresses << QHostAddress() + << QList({"At least one entry is required for \"addresses\""_L1, "Discarding unparsable message"_L1}); + + QTest::newRow("Invalid address - No sender") + << invalidAddresses << QHostAddress() + << QList({"Found \"addresses\" entry with wrong scheme: "_L1, "Discarding unparsable message"_L1}); + + QTest::newRow("Valid address - No sender") + << validAddresses << QHostAddress() + << QList(); + + QTest::newRow("Empty addresses - Valid sender") + << emptyAddresses + << QHostAddress("192.168.1.88"_L1) + << QList({"At least one entry is required for \"addresses\""_L1, "Discarding unparsable message"_L1}); + + QTest::newRow("Invalid address - Valid sender") + << invalidAddresses << QHostAddress("192.168.1.88"_L1) + << QList({"Found \"addresses\" entry with wrong scheme: "_L1, "Discarding unparsable message"_L1}); + + QTest::newRow("Valid address - Valid sender") + << validAddresses << QHostAddress("192.168.1.88"_L1) + << QList(); + + } + + + void testOnNewMessage() + { + QFETCH(QByteArray, discovery); + QFETCH(QHostAddress, sender); + QFETCH(QList, logging); + + QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); + RemoteIfdClient client; + QSignalSpy deviceAppeared(&client, &RemoteIfdClient::fireDeviceAppeared); client.startDetection(); QVERIFY(!mDatagramHandlerMock.isNull()); - Q_EMIT mDatagramHandlerMock->fireNewMessage(unparsableJson, QHostAddress("192.168.1.88"_L1)); - QCOMPARE(logSpy.count(), 6); - QVERIFY(logSpy.at(5).at(0).toString().contains("Discarding unparsable message"_L1)); + Q_EMIT mDatagramHandlerMock->fireNewMessage(discovery, sender); + QCOMPARE(logSpy.count(), logging.size()); + for (const auto& log : logging) + { + QVERIFY(TestFileHelper::containsLog(logSpy, log)); + } + + if (logging.isEmpty()) + { + QCOMPARE(deviceAppeared.size(), 1); + QCOMPARE(client.getAnnouncingRemoteDevices().size(), 1); + QCOMPARE(client.getAnnouncingRemoteDevices().at(0)->getDiscovery().getAddresses().size(), 1); + QCOMPARE(*client.getAnnouncingRemoteDevices().at(0)->getDiscovery().getAddresses().constBegin(), QUrl("wss://192.168.1.87:24728"_L1)); + } } @@ -248,14 +309,16 @@ })"); version = IfdVersion::Version::v2; } - const QByteArray offerJson = Discovery("Sony Xperia Z5 compact"_L1, ifdId, 24728, {version}).toByteArray(version); + Discovery discovery("Sony Xperia Z5 compact"_L1, ifdId, 24728, {version}); + discovery.setAddresses({QHostAddress(hostAddress)}); + const QByteArray offerJson = discovery.toByteArray(version); RemoteIfdClient client; client.startDetection(); QVERIFY(!mDatagramHandlerMock.isNull()); - QVERIFY(!mIfdListMock.isNull()); - QSignalSpy spyAppearedList(mIfdListMock.data(), &IfdListMock::fireDeviceAppeared); + QVERIFY(!mIfdList.isNull()); + QSignalSpy spyAppearedList(mIfdList.data(), &IfdList::fireDeviceAppeared); QSignalSpy spyAppeared(&client, &IfdClient::fireDeviceAppeared); Q_EMIT mDatagramHandlerMock->fireNewMessage(offerJson, QHostAddress(hostAddress)); @@ -264,7 +327,7 @@ QSignalSpy spyVanished(&client, &IfdClient::fireDeviceVanished); QCOMPARE(spyVanished.count(), 0); - Q_EMIT mIfdListMock->fireDeviceVanished(QSharedPointer()); + Q_EMIT mIfdList->fireDeviceVanished(QSharedPointer()); QCOMPARE(spyVanished.count(), 1); } @@ -297,15 +360,15 @@ QVERIFY(!mRemoteConnectorMock.isNull()); QSignalSpy spyConnectionRequest(mRemoteConnectorMock.data(), &RemoteConnectorMock::fireConnectionRequestReceived); - const Discovery discovery(QString(), QByteArrayLiteral("0123456789ABCDEF"), 12345, {IfdVersion::Version::latest, IfdVersion::Version::v2}); - const IfdDescriptor descr(discovery, QHostAddress("192.168.1.88"_L1)); - QSharedPointer emptyEntry(new IfdListEntry(descr)); + Discovery discovery(QString(), QByteArrayLiteral("0123456789ABCDEF"), 12345, {IfdVersion::Version::latest, IfdVersion::Version::v2}); + discovery.setAddresses({QHostAddress("192.168.1.88"_L1)}); + QSharedPointer emptyEntry(new IfdListEntry(discovery)); client.establishConnection(emptyEntry, "password1"); QTRY_COMPARE(spyConnectionRequest.count(), 1); // clazy:exclude=qstring-allocations QSignalSpy spyConnectionDone(&client, &IfdClient::fireEstablishConnectionDone); - Q_EMIT mRemoteConnectorMock->fireDispatcherError(emptyEntry->getIfdDescriptor(), + Q_EMIT mRemoteConnectorMock->fireDispatcherError(emptyEntry->getDiscovery().getIfdId(), IfdErrorCode::CONNECTION_ERROR); QCOMPARE(spyConnectionDone.count(), 1); } diff -Nru ausweisapp2-2.3.1/test/qt/ifd/remote/test_RemoteIfdReaderManagerPlugin.cpp ausweisapp2-2.4.0/test/qt/ifd/remote/test_RemoteIfdReaderManagerPlugin.cpp --- ausweisapp2-2.3.1/test/qt/ifd/remote/test_RemoteIfdReaderManagerPlugin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/remote/test_RemoteIfdReaderManagerPlugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -7,6 +7,7 @@ #include "AppSettings.h" #include "Env.h" #include "MockIfdDispatcher.h" +#include "PortFile.h" #include "Reader.h" #include "RemoteIfdClient.h" #include "VolatileSettings.h" @@ -94,9 +95,9 @@ void MockIfdClient::populateRemoteDevices() { - const Discovery discovery("TestIfdName"_L1, QByteArray::fromHex("3ff02e8dc335f7ebb39299fbc12b66bf378445e59a68880e81464c50874e09cd"_ba), 1337, {IfdVersion::Version::latest}); - const IfdDescriptor ifdDescriptor(discovery, QHostAddress("127.0.0.1"_L1), true); - mRemoteDevices = {QSharedPointer::create(ifdDescriptor)}; + Discovery discovery("TestIfdName"_L1, QByteArray::fromHex("3ff02e8dc335f7ebb39299fbc12b66bf378445e59a68880e81464c50874e09cd"_ba), PortFile::cDefaultPort, {IfdVersion::Version::latest}); + discovery.setAddresses({QHostAddress("127.0.0.1"_L1)}); + mRemoteDevices = {QSharedPointer::create(discovery)}; auto& remoteServiceSettings = Env::getSingleton()->getRemoteServiceSettings(); const QByteArray certData = R"(-----BEGIN CERTIFICATE----- MIIFWDCCA0ACCQD1pPOO77lbczANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJk @@ -195,12 +196,12 @@ { QSignalSpy spySend(mDispatcher1.data(), &MockIfdDispatcher::fireSend); - QVERIFY(mPlugin->getReaders().isEmpty()); + QVERIFY(mPlugin->mReaders.isEmpty()); Q_EMIT mIfdClient->fireNewDispatcher(mDispatcher1); QTRY_COMPARE(spySend.count(), 1); // clazy:exclude=qstring-allocations - QVERIFY(mPlugin->getReaders().isEmpty()); + QVERIFY(mPlugin->mReaders.isEmpty()); QCOMPARE(spySend.size(), 1); QSharedPointer result = qvariant_cast>(spySend.takeFirst().at(0)); @@ -228,13 +229,13 @@ message.reset(new IfdStatus(info)); message->mConnectedReader = false; mDispatcher1->onReceived(message); - QCOMPARE(mPlugin->getReaders().size(), 0); + QVERIFY(mPlugin->mReaders.contains(QStringLiteral("NFC Reader#TestContext"))); + QCOMPARE(mPlugin->getReader(QStringLiteral("NFC Reader#TestContext")), nullptr); QCOMPARE(spySend.size(), 0); QCOMPARE(spyAdded.size(), 1); QCOMPARE(getReaderInfo(spyAdded).getName(), QStringLiteral("NFC Reader#TestContext")); QCOMPARE(spyUpdated.size(), 0); QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(mPlugin->getReaders().size(), 0); mDispatcher1->onClosed(); QCOMPARE(spySend.size(), 0); @@ -244,7 +245,7 @@ const auto removedInfo = getReaderInfo(spyRemoved); QCOMPARE(removedInfo.getName(), QStringLiteral("NFC Reader#TestContext")); QCOMPARE(removedInfo.isValid(), false); - QCOMPARE(mPlugin->getReaders().size(), 0); + QVERIFY(mPlugin->mReaders.isEmpty()); } @@ -267,22 +268,24 @@ Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(false); message.reset(new IfdStatus(info)); mDispatcher1->onReceived(message); - QCOMPARE(mPlugin->getReaders().size(), 1); + QCOMPARE(mPlugin->mReaders.size(), 1); QCOMPARE(spySend.size(), 0); QCOMPARE(spyAdded.size(), 1); QCOMPARE(getReaderInfo(spyAdded).getName(), QStringLiteral("NFC Reader#TestContext")); QCOMPARE(spyUpdated.size(), 0); QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader#TestContext")); - QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().isValid(), true); - QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().isBasicReader(), true); - QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 500); + QCOMPARE(mPlugin->mReaders.size(), 1); + const auto& reader = mPlugin->getReader(QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(reader->getName(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(reader->getReaderInfo().isValid(), true); + QCOMPARE(reader->getReaderInfo().isBasicReader(), true); + QCOMPARE(reader->getReaderInfo().getMaxApduLength(), 500); message.reset(new IfdStatus(info)); message->mConnectedReader = false; mDispatcher1->onReceived(message); - QCOMPARE(mPlugin->getReaders().size(), 0); + QVERIFY(mPlugin->mReaders.contains(QStringLiteral("NFC Reader#TestContext"))); + QCOMPARE(mPlugin->getReader(QStringLiteral("NFC Reader#TestContext")), nullptr); QCOMPARE(spySend.size(), 0); QCOMPARE(spyAdded.size(), 0); QCOMPARE(spyUpdated.size(), 1); @@ -290,7 +293,6 @@ QCOMPARE(updateInfo1.getName(), QStringLiteral("NFC Reader#TestContext")); QCOMPARE(updateInfo1.isValid(), false); QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(mPlugin->getReaders().size(), 0); message.reset(new IfdStatus(info)); message->mConnectedReader = true; @@ -301,8 +303,8 @@ QCOMPARE(updateInfo2.getName(), QStringLiteral("NFC Reader#TestContext")); QCOMPARE(updateInfo2.isValid(), true); QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(mPlugin->mReaders.size(), 1); + QCOMPARE(mPlugin->getReader(QStringLiteral("NFC Reader#TestContext"))->getName(), QStringLiteral("NFC Reader#TestContext")); mDispatcher1->onClosed(); QCOMPARE(spySend.size(), 0); @@ -312,7 +314,7 @@ const auto removedInfo = getReaderInfo(spyRemoved); QCOMPARE(removedInfo.getName(), QStringLiteral("NFC Reader#TestContext")); QCOMPARE(removedInfo.isValid(), true); - QCOMPARE(mPlugin->getReaders().size(), 0); + QCOMPARE(mPlugin->mReaders.size(), 0); } @@ -339,10 +341,11 @@ const auto spyInfo = getReaderInfo(spyAdded); QCOMPARE(spyInfo.getName(), QStringLiteral("NFC Reader#TestContext")); QCOMPARE(spyInfo.getMaxApduLength(), 500); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader#TestContext")); - QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().isBasicReader(), false); - QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 500); + QCOMPARE(mPlugin->mReaders.size(), 1); + const auto& reader = mPlugin->getReader(QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(reader->getName(), QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(reader->getReaderInfo().isBasicReader(), false); + QCOMPARE(reader->getReaderInfo().getMaxApduLength(), 500); } @@ -383,9 +386,9 @@ QCOMPARE(getReaderInfo(spyAdded).getName(), QStringLiteral("NFC Reader 2#TestContext")); QCOMPARE(spyUpdated.size(), 0); QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(mPlugin->getReaders().size(), 2); - QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader 1#TestContext")); - QCOMPARE(mPlugin->getReaders().at(1)->getName(), QStringLiteral("NFC Reader 2#TestContext")); + QCOMPARE(mPlugin->mReaders.size(), 2); + QCOMPARE(mPlugin->getReader(QStringLiteral("NFC Reader 1#TestContext"))->getName(), QStringLiteral("NFC Reader 1#TestContext")); + QCOMPARE(mPlugin->getReader(QStringLiteral("NFC Reader 2#TestContext"))->getName(), QStringLiteral("NFC Reader 2#TestContext")); message.reset(new IfdStatus(info1)); message->mConnectedReader = false; @@ -399,8 +402,10 @@ QCOMPARE(updateInfo1.getName(), QStringLiteral("NFC Reader 1#TestContext")); QCOMPARE(updateInfo1.isValid(), false); QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader 2#TestContext")); + QCOMPARE(mPlugin->mReaders.size(), 2); + QVERIFY(mPlugin->mReaders.contains(QStringLiteral("NFC Reader 1#TestContext"))); + QCOMPARE(mPlugin->getReader(QStringLiteral("NFC Reader 1#TestContext")), nullptr); + QCOMPARE(mPlugin->getReader(QStringLiteral("NFC Reader 2#TestContext"))->getName(), QStringLiteral("NFC Reader 2#TestContext")); message.reset(new IfdStatus(info1)); message->mConnectedReader = true; @@ -414,9 +419,9 @@ QCOMPARE(updateInfo2.getName(), QStringLiteral("NFC Reader 1#TestContext")); QCOMPARE(updateInfo2.isValid(), true); QCOMPARE(spyRemoved.size(), 0); - QCOMPARE(mPlugin->getReaders().size(), 2); - QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader 1#TestContext")); - QCOMPARE(mPlugin->getReaders().at(1)->getName(), QStringLiteral("NFC Reader 2#TestContext")); + QCOMPARE(mPlugin->mReaders.size(), 2); + QCOMPARE(mPlugin->getReader(QStringLiteral("NFC Reader 1#TestContext"))->getName(), QStringLiteral("NFC Reader 1#TestContext")); + QCOMPARE(mPlugin->getReader(QStringLiteral("NFC Reader 2#TestContext"))->getName(), QStringLiteral("NFC Reader 2#TestContext")); mDispatcher1->onClosed(); QCOMPARE(spySend1.size(), 0); @@ -427,8 +432,8 @@ const auto removedInfo1 = getReaderInfo(spyRemoved); QCOMPARE(removedInfo1.getName(), QStringLiteral("NFC Reader 1#TestContext")); QCOMPARE(removedInfo1.isValid(), true); - QCOMPARE(mPlugin->getReaders().size(), 1); - QCOMPARE(mPlugin->getReaders().at(0)->getName(), QStringLiteral("NFC Reader 2#TestContext")); + QCOMPARE(mPlugin->mReaders.size(), 1); + QCOMPARE(mPlugin->getReader(QStringLiteral("NFC Reader 2#TestContext"))->getName(), QStringLiteral("NFC Reader 2#TestContext")); mDispatcher2->onClosed(); QCOMPARE(spySend1.size(), 0); @@ -439,7 +444,7 @@ const auto removedInfo2 = getReaderInfo(spyRemoved); QCOMPARE(removedInfo2.getName(), QStringLiteral("NFC Reader 2#TestContext")); QCOMPARE(removedInfo2.isValid(), true); - QCOMPARE(mPlugin->getReaders().size(), 0); + QCOMPARE(mPlugin->mReaders.size(), 0); } @@ -464,12 +469,13 @@ info1.setBasicReader(true); Env::getSingleton()->getRemoteServiceSettings().setPinPadMode(false); mDispatcher1->onReceived(message); - QCOMPARE(mPlugin->getReaders().at(0)->getCard(), nullptr); + const auto& reader = mPlugin->getReader(QStringLiteral("NFC Reader#TestContext")); + QCOMPARE(reader->getCard(), nullptr); - QSignalSpy spyInserted(mPlugin->getReaders().at(0), &Reader::fireCardInserted); - QSignalSpy spyRemoved(mPlugin->getReaders().at(0), &Reader::fireCardRemoved); - QSignalSpy spyChanged(mPlugin->getReaders().at(0), &Reader::fireCardInfoChanged); - QSignalSpy spyUpdated(mPlugin->getReaders().at(0), &Reader::fireReaderPropertiesUpdated); + QSignalSpy spyInserted(reader, &Reader::fireCardInserted); + QSignalSpy spyRemoved(reader, &Reader::fireCardRemoved); + QSignalSpy spyChanged(reader, &Reader::fireCardInfoChanged); + QSignalSpy spyUpdated(reader, &Reader::fireReaderPropertiesUpdated); info1.setCardInfo(CardInfo(CardType::EID_CARD)); message.reset(new IfdStatus(info1)); @@ -478,9 +484,9 @@ QTRY_COMPARE(spySend.count(), 1); // clazy:exclude=qstring-allocations result = qvariant_cast>(spySend.takeFirst().at(0)); QCOMPARE(result->getType(), IfdMessageType::IFDConnect); - QVERIFY(mPlugin->getReaders().at(0)->getCard() != nullptr); - QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 500); - QVERIFY(mPlugin->getReaders().at(0)->getReaderInfo().hasCard()); + QVERIFY(reader->getCard() != nullptr); + QCOMPARE(reader->getReaderInfo().getMaxApduLength(), 500); + QVERIFY(reader->getReaderInfo().hasCard()); QCOMPARE(spyInserted.size(), 1); QCOMPARE(getReaderInfo(spyInserted).getName(), QStringLiteral("NFC Reader#TestContext")); QCOMPARE(spyRemoved.size(), 0); @@ -490,9 +496,9 @@ 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); - QVERIFY(mPlugin->getReaders().at(0)->getReaderInfo().hasCard()); + QVERIFY(reader->getCard() != nullptr); + QCOMPARE(reader->getReaderInfo().getMaxApduLength(), 1); + QVERIFY(reader->getReaderInfo().hasCard()); QCOMPARE(spyInserted.size(), 0); QCOMPARE(spyRemoved.size(), 0); QCOMPARE(spyChanged.size(), 0); @@ -501,9 +507,9 @@ message.reset(new IfdStatus(info1)); mDispatcher1->onReceived(message); - QVERIFY(mPlugin->getReaders().at(0)->getCard() != nullptr); - QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 1); - QVERIFY(mPlugin->getReaders().at(0)->getReaderInfo().hasCard()); + QVERIFY(reader->getCard() != nullptr); + QCOMPARE(reader->getReaderInfo().getMaxApduLength(), 1); + QVERIFY(reader->getReaderInfo().hasCard()); QCOMPARE(spyInserted.size(), 0); QCOMPARE(spyRemoved.size(), 0); QCOMPARE(spyChanged.size(), 0); @@ -514,8 +520,8 @@ 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()); + QCOMPARE(reader->getCard(), nullptr); + QVERIFY(!reader->getReaderInfo().hasCard()); QCOMPARE(spyInserted.size(), 0); QCOMPARE(spyRemoved.size(), 1); QCOMPARE(getReaderInfo(spyRemoved).getName(), QStringLiteral("NFC Reader#TestContext")); @@ -528,9 +534,9 @@ QTRY_COMPARE(spySend.count(), 1); // clazy:exclude=qstring-allocations result = qvariant_cast>(spySend.takeFirst().at(0)); QCOMPARE(result->getType(), IfdMessageType::IFDConnect); - QVERIFY(mPlugin->getReaders().at(0)->getCard() != nullptr); - QCOMPARE(mPlugin->getReaders().at(0)->getReaderInfo().getMaxApduLength(), 1); - QVERIFY(mPlugin->getReaders().at(0)->getReaderInfo().hasCard()); + QVERIFY(reader->getCard() != nullptr); + QCOMPARE(reader->getReaderInfo().getMaxApduLength(), 1); + QVERIFY(reader->getReaderInfo().hasCard()); QCOMPARE(spyInserted.size(), 1); QCOMPARE(getReaderInfo(spyInserted).getName(), QStringLiteral("NFC Reader#TestContext")); QCOMPARE(spyRemoved.size(), 0); @@ -559,8 +565,8 @@ result = qvariant_cast>(spySend.takeFirst().at(0)); QCOMPARE(result->getType(), IfdMessageType::IFDDisconnect); - QCOMPARE(mPlugin->getReaders().size(), 1); - Card* card = mPlugin->getReaders().at(0)->getCard(); + QCOMPARE(mPlugin->mReaders.size(), 1); + Card* card = mPlugin->getReader(QStringLiteral("NFC Reader#TestContext"))->getCard(); QVERIFY(card != nullptr); QCOMPARE(card->establishConnection(), CardReturnCode::OK); @@ -719,6 +725,29 @@ } + void testShelve() + { + QSignalSpy spySend(mDispatcher1.data(), &MockIfdDispatcher::fireSend); + + mPlugin->shelveAll(); + + mDispatcher1->setState(MockIfdDispatcher::DispatcherState::ReaderWithCard); + Q_EMIT mIfdClient->fireNewDispatcher(mDispatcher1); + + QTRY_COMPARE(spySend.count(), 6); // clazy:exclude=qstring-allocations + mPlugin->shelveAll(); + + QTest::ignoreMessage(QtDebugMsg, "Card shelved"); + mPlugin->getReader(QStringLiteral("NFC Reader#TestContext"))->shelveCard(); + + QTest::ignoreMessage(QtInfoMsg, "Card inserted: {Type: UNKNOWN, Retry counter: -1, PIN deactivated: false, PIN initial: false}"); + mPlugin->insert(QStringLiteral("NFC Reader#TestContext"), QVariant()); + + QTest::ignoreMessage(QtDebugMsg, "Card shelved"); + mPlugin->shelveAll(); + } + + }; QTEST_GUILESS_MAIN(test_RemoteIfdReaderManagerPlugin) diff -Nru ausweisapp2-2.3.1/test/qt/ifd/remote/test_RemoteReaderAdvertiser.cpp ausweisapp2-2.4.0/test/qt/ifd/remote/test_RemoteReaderAdvertiser.cpp --- ausweisapp2-2.3.1/test/qt/ifd/remote/test_RemoteReaderAdvertiser.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/remote/test_RemoteReaderAdvertiser.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -11,6 +11,7 @@ #include #include + namespace governikus { @@ -36,21 +37,25 @@ public: QByteArrayList mList; - [[nodiscard]] bool isBound() const override; + [[nodiscard]] bool isBound() const override + { + return true; + } + - void send(const QByteArray& pData) override + [[nodiscard]] QList getAllBroadcastEntries() const override { - mList << pData; + return QList(); } -}; + void send(const QByteArray& pData, const QList&) override + { + mList << pData; + } -bool DatagramHandlerMock::isBound() const -{ - return true; -} +}; class test_RemoteReaderAdvertiser @@ -105,7 +110,7 @@ QCOMPARE(offerMsg.getIfdId(), ifdId); QCOMPARE(offerMsg.getPort(), port); QCOMPARE(offerMsg.getSupportedApis(), IfdVersion::supported()); - QCOMPARE(offerMsg.getPairing(), pairing); + QCOMPARE(offerMsg.isPairing(), pairing); } diff -Nru ausweisapp2-2.3.1/test/qt/ifd/remote/test_RemoteTlsServer.cpp ausweisapp2-2.4.0/test/qt/ifd/remote/test_RemoteTlsServer.cpp --- ausweisapp2-2.3.1/test/qt/ifd/remote/test_RemoteTlsServer.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/remote/test_RemoteTlsServer.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -74,7 +74,7 @@ QSignalSpy newConnection(&server, &RemoteTlsServer::fireNewConnection); QSignalSpy clientEncrypted(&client, &QSslSocket::encrypted); - QSignalSpy clientErrorOccured(&client, &QAbstractSocket::errorOccurred); + QSignalSpy clientErrorOccurred(&client, &QAbstractSocket::errorOccurred); client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort()); @@ -83,7 +83,7 @@ QTRY_COMPARE(newConnection.count(), 1); // clazy:exclude=qstring-allocations QVERIFY(pskSignalFired); QTRY_COMPARE(clientEncrypted.count(), 1); // clazy:exclude=qstring-allocations - QCOMPARE(clientErrorOccured.count(), 0); + QCOMPARE(clientErrorOccurred.count(), 0); QVERIFY(remoteSocket); const QByteArray sendData("hello world"); @@ -96,7 +96,7 @@ else { QTest::ignoreMessage(pLogMsgType, QRegularExpression(pLogError)); - QTRY_COMPARE(clientErrorOccured.count(), 1); // clazy:exclude=qstring-allocations + QTRY_COMPARE(clientErrorOccurred.count(), 1); // clazy:exclude=qstring-allocations QCOMPARE(client.error(), QAbstractSocket::RemoteHostClosedError); } @@ -307,7 +307,7 @@ }); QSignalSpy clientEncrypted(&client, &QSslSocket::encrypted); - QSignalSpy clientErrorOccured(&client, &QAbstractSocket::errorOccurred); + QSignalSpy clientErrorOccurred(&client, &QAbstractSocket::errorOccurred); QSignalSpy newConnection(&server, &RemoteTlsServer::fireNewConnection); client.connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), server.serverPort()); @@ -315,12 +315,12 @@ { QTRY_COMPARE(newConnection.count(), 1); // clazy:exclude=qstring-allocations QTRY_COMPARE(clientEncrypted.count(), 1); // clazy:exclude=qstring-allocations - QCOMPARE(clientErrorOccured.count(), 0); + QCOMPARE(clientErrorOccurred.count(), 0); } else { QTest::ignoreMessage(connectLogMsgType, QRegularExpression(connectLogError)); - QTRY_COMPARE(clientErrorOccured.count(), 1); // clazy:exclude=qstring-allocations + QTRY_COMPARE(clientErrorOccurred.count(), 1); // clazy:exclude=qstring-allocations QCOMPARE(client.error(), QAbstractSocket::RemoteHostClosedError); } } diff -Nru ausweisapp2-2.3.1/test/qt/ifd/remote/test_RemoteWebSocketServer.cpp ausweisapp2-2.4.0/test/qt/ifd/remote/test_RemoteWebSocketServer.cpp --- ausweisapp2-2.3.1/test/qt/ifd/remote/test_RemoteWebSocketServer.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/remote/test_RemoteWebSocketServer.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -176,7 +176,7 @@ QVERIFY(mServer->listen(QStringLiteral("TestServer"))); QVERIFY(mServer->isPairingAnnounced()); - client.open(QStringLiteral("wss://127.0.0.1:").append(QString::number(mServer->getServerPort()))); + client.open(QUrl(QStringLiteral("wss://127.0.0.1:").append(QString::number(mServer->getServerPort())))); QTRY_COMPARE(spy.count(), 1); // clazy:exclude=qstring-allocations QCOMPARE(client.state(), QAbstractSocket::SocketState::ConnectedState); @@ -200,7 +200,7 @@ QSignalSpy spy(&client, &QWebSocket::connected); PskHandler pskHandler(&client); - client.open(QStringLiteral("wss://127.0.0.1:").append(QString::number(mServer->getServerPort()))); + client.open(QUrl(QStringLiteral("wss://127.0.0.1:").append(QString::number(mServer->getServerPort())))); QTRY_COMPARE(spy.count(), 1); // clazy:exclude=qstring-allocations QCOMPARE(client.state(), QAbstractSocket::SocketState::ConnectedState); @@ -245,10 +245,10 @@ QSignalSpy spy1(&client1, &QWebSocket::connected); QSignalSpy spy2(&client2, &QWebSocket::disconnected); - client1.open(QStringLiteral("wss://127.0.0.1:").append(QString::number(mServer->getServerPort()))); + client1.open(QUrl(QStringLiteral("wss://127.0.0.1:").append(QString::number(mServer->getServerPort())))); QTRY_COMPARE(spy1.count(), 1); // clazy:exclude=qstring-allocations - client2.open(QStringLiteral("wss://127.0.0.1:").append(QString::number(mServer->getServerPort()))); + client2.open(QUrl(QStringLiteral("wss://127.0.0.1:").append(QString::number(mServer->getServerPort())))); QTRY_COMPARE(spy2.count(), 1); // clazy:exclude=qstring-allocations QCOMPARE(client1.state(), QAbstractSocket::SocketState::ConnectedState); @@ -277,7 +277,7 @@ PskHandler pskHandler(&client, mServer.data()); mServer->setPairing(); - client.open(QStringLiteral("wss://127.0.0.1:").append(QString::number(mServer->getServerPort()))); + client.open(QUrl(QStringLiteral("wss://127.0.0.1:").append(QString::number(mServer->getServerPort())))); QVERIFY(mServer->isPairingAnnounced()); QTRY_COMPARE(newConnectionSpy.count(), 1); // clazy:exclude=qstring-allocations diff -Nru ausweisapp2-2.3.1/test/qt/ifd/test_ConnectRequest.cpp ausweisapp2-2.4.0/test/qt/ifd/test_ConnectRequest.cpp --- ausweisapp2-2.3.1/test/qt/ifd/test_ConnectRequest.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/test_ConnectRequest.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,233 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#include "ConnectRequest.h" + +#include +#include + + +using namespace Qt::Literals::StringLiterals; +using namespace governikus; + + +class MockConnectRequest + : public ConnectRequest +{ + Q_OBJECT + + private: + QAbstractSocket::SocketState mState = QAbstractSocket::ConnectedState; + + QAbstractSocket::SocketState getState(const QSharedPointer& pSocket) const override; + + public: + using ConnectRequest::ConnectRequest; + + void setState(QAbstractSocket::SocketState pState) + { + mState = pState; + } + + +}; + + +QAbstractSocket::SocketState MockConnectRequest::getState(const QSharedPointer& pSocket) const +{ + Q_UNUSED(pSocket) + return mState; +} + + +class test_ConnectRequest + : public QObject +{ + Q_OBJECT + + private: + Discovery mDiscovery = Discovery("Dev1"_L1, QByteArrayLiteral("0123456789ABCDEF"), 1234, {IfdVersion::Version::latest}); + + private Q_SLOTS: + void processResult_success() + { + MockConnectRequest connectRequest(mDiscovery, "123456"_ba, 5000); + QSignalSpy spySucceed(&connectRequest, &MockConnectRequest::fireConnectionCreated); + QSignalSpy spyError(&connectRequest, &MockConnectRequest::fireConnectionError); + + const auto& socket = QSharedPointer::create(); + connectRequest.mSockets << socket; + + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::ConnectedState"); + QTest::ignoreMessage(QtDebugMsg, " Connection succeeded"); + connectRequest.processResult(socket); + QCOMPARE(spySucceed.count(), 1); + QCOMPARE(spyError.count(), 0); + QVERIFY(connectRequest.mSockets.isEmpty()); + + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::ConnectedState"); + QTest::ignoreMessage(QtWarningMsg, "Ignoring result from unexpected socket. ConnectRequest has probably already been completed"); + connectRequest.processResult(socket); + QCOMPARE(spySucceed.count(), 1); + QCOMPARE(spyError.count(), 0); + QVERIFY(connectRequest.mSockets.isEmpty()); + } + + + void processResult_fail() + { + MockConnectRequest connectRequest(mDiscovery, "123456"_ba, 5000); + connectRequest.setState(QAbstractSocket::UnconnectedState); + QSignalSpy spySucceed(&connectRequest, &MockConnectRequest::fireConnectionCreated); + QSignalSpy spyError(&connectRequest, &MockConnectRequest::fireConnectionError); + + const auto& socket = QSharedPointer::create(); + connectRequest.mSockets << socket; + + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::UnconnectedState"); + QTest::ignoreMessage(QtDebugMsg, " Connection failed. No more pending connections left"); + connectRequest.processResult(socket); + QCOMPARE(spySucceed.count(), 0); + QCOMPARE(spyError.count(), 1); + QVERIFY(connectRequest.mSockets.isEmpty()); + + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::UnconnectedState"); + QTest::ignoreMessage(QtWarningMsg, "Ignoring result from unexpected socket. ConnectRequest has probably already been completed"); + connectRequest.processResult(socket); + QCOMPARE(spySucceed.count(), 0); + QCOMPARE(spyError.count(), 1); + QVERIFY(connectRequest.mSockets.isEmpty()); + } + + + void processResult_timeout() + { + MockConnectRequest connectRequest(mDiscovery, "123456"_ba, 5000); + QSignalSpy spySucceed(&connectRequest, &MockConnectRequest::fireConnectionCreated); + QSignalSpy spyError(&connectRequest, &MockConnectRequest::fireConnectionError); + + const auto& socket = QSharedPointer::create(); + connectRequest.mSockets << socket; + + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Connection could not be established after \\d*0 ms"_L1)); + connectRequest.onTimeout(); + QCOMPARE(spySucceed.count(), 0); + QCOMPARE(spyError.count(), 1); + QVERIFY(connectRequest.mSockets.isEmpty()); + } + + + void processResult_successAfterFail() + { + MockConnectRequest connectRequest(mDiscovery, "123456"_ba, 5000); + connectRequest.setState(QAbstractSocket::UnconnectedState); + QSignalSpy spySucceed(&connectRequest, &MockConnectRequest::fireConnectionCreated); + QSignalSpy spyError(&connectRequest, &MockConnectRequest::fireConnectionError); + + const auto& socket = QSharedPointer::create(); + connectRequest.mSockets << socket; + connectRequest.mSockets << socket; + + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::UnconnectedState"); + QTest::ignoreMessage(QtDebugMsg, " Connection failed. Waiting for pending connections"); + connectRequest.processResult(socket); + QCOMPARE(spySucceed.count(), 0); + QCOMPARE(spyError.count(), 0); + QCOMPARE(connectRequest.mSockets.size(), 1); + + connectRequest.setState(QAbstractSocket::ConnectedState); + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::ConnectedState"); + QTest::ignoreMessage(QtDebugMsg, " Connection succeeded"); + connectRequest.processResult(socket); + QCOMPARE(spySucceed.count(), 1); + QCOMPARE(spyError.count(), 0); + QVERIFY(connectRequest.mSockets.isEmpty()); + } + + + void onError0() + { + MockConnectRequest connectRequest(mDiscovery, "123456"_ba, 5000); + connectRequest.setState(QAbstractSocket::UnconnectedState); + QSignalSpy spySucceed(&connectRequest, &MockConnectRequest::fireConnectionCreated); + QSignalSpy spyError(&connectRequest, &MockConnectRequest::fireConnectionError); + + const auto& socket = QSharedPointer::create(); + connectRequest.mSockets << socket; + + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Connection error: QAbstractSocket::UnknownSocketError .*"_L1)); + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::UnconnectedState"); + QTest::ignoreMessage(QtDebugMsg, " Connection failed. No more pending connections left"); + connectRequest.onError(socket, QAbstractSocket::UnknownSocketError); + QCOMPARE(spySucceed.count(), 0); + QCOMPARE(spyError.count(), 1); + QCOMPARE(spyError.at(0).at(1).value(), IfdErrorCode::CONNECTION_ERROR); + QVERIFY(connectRequest.mSockets.isEmpty()); + } + + + void onError1() + { + MockConnectRequest connectRequest(mDiscovery, "123456"_ba, 5000); + connectRequest.setState(QAbstractSocket::UnconnectedState); + QSignalSpy spySucceed(&connectRequest, &MockConnectRequest::fireConnectionCreated); + QSignalSpy spyError(&connectRequest, &MockConnectRequest::fireConnectionError); + + const auto& socket = QSharedPointer::create(); + connectRequest.mSockets << socket; + connectRequest.mSockets << socket; + + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Connection error: QAbstractSocket::UnknownSocketError .*"_L1)); + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::UnconnectedState"); + QTest::ignoreMessage(QtDebugMsg, " Connection failed. Waiting for pending connections"); + connectRequest.onError(socket, QAbstractSocket::UnknownSocketError); + QCOMPARE(spySucceed.count(), 0); + QCOMPARE(spyError.count(), 0); + QCOMPARE(connectRequest.mSockets.size(), 1); + + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Connection error: QAbstractSocket::RemoteHostClosedError .*"_L1)); + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::UnconnectedState"); + QTest::ignoreMessage(QtDebugMsg, " Connection failed. No more pending connections left"); + connectRequest.onError(socket, QAbstractSocket::RemoteHostClosedError); + QCOMPARE(spySucceed.count(), 0); + QCOMPARE(spyError.count(), 1); + QCOMPARE(spyError.at(0).at(1).value(), IfdErrorCode::REMOTE_HOST_REFUSED_CONNECTION); + QVERIFY(connectRequest.mSockets.isEmpty()); + } + + + void onError2() + { + MockConnectRequest connectRequest(mDiscovery, "123456"_ba, 5000); + connectRequest.setState(QAbstractSocket::UnconnectedState); + QSignalSpy spySucceed(&connectRequest, &MockConnectRequest::fireConnectionCreated); + QSignalSpy spyError(&connectRequest, &MockConnectRequest::fireConnectionError); + + const auto& socket = QSharedPointer::create(); + connectRequest.mSockets << socket; + connectRequest.mSockets << socket; + + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Connection error: QAbstractSocket::RemoteHostClosedError .*"_L1)); + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::UnconnectedState"); + QTest::ignoreMessage(QtDebugMsg, " Connection failed. Waiting for pending connections"); + connectRequest.onError(socket, QAbstractSocket::RemoteHostClosedError); + QCOMPARE(spySucceed.count(), 0); + QCOMPARE(spyError.count(), 0); + QCOMPARE(connectRequest.mSockets.size(), 1); + + QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Connection error: QAbstractSocket::UnknownSocketError .*"_L1)); + QTest::ignoreMessage(QtDebugMsg, " Connection to QUrl(\"\") finished with QAbstractSocket::UnconnectedState"); + QTest::ignoreMessage(QtDebugMsg, " Connection failed. No more pending connections left"); + connectRequest.onError(socket, QAbstractSocket::UnknownSocketError); + QCOMPARE(spySucceed.count(), 0); + QCOMPARE(spyError.count(), 1); + QCOMPARE(spyError.at(0).at(1).value(), IfdErrorCode::REMOTE_HOST_REFUSED_CONNECTION); + QVERIFY(connectRequest.mSockets.isEmpty()); + } + + +}; + +QTEST_GUILESS_MAIN(test_ConnectRequest) +#include "test_ConnectRequest.moc" diff -Nru ausweisapp2-2.3.1/test/qt/ifd/test_IfdConnector.cpp ausweisapp2-2.4.0/test/qt/ifd/test_IfdConnector.cpp --- ausweisapp2-2.3.1/test/qt/ifd/test_IfdConnector.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/test_IfdConnector.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -17,12 +17,12 @@ #include #include -#include using namespace Qt::Literals::StringLiterals; using namespace governikus; -Q_DECLARE_METATYPE(IfdDescriptor) + +Q_DECLARE_METATYPE(Discovery) Q_DECLARE_METATYPE(QSharedPointer) @@ -65,47 +65,38 @@ )"_ba; version = IfdVersion::Version::v2; } - return Discovery(pIfdName, ifdId, pPort, {version}); + Discovery discovery(pIfdName, ifdId, pPort, {version}); + discovery.setAddresses({QHostAddress(QHostAddress::LocalHost)}); + return discovery; } void sendRequest(const QSharedPointer& pConnector, - const QHostAddress& pHostAddress, const Discovery& pDiscovery, const QByteArray& pPassword) { - const IfdDescriptor descr(pDiscovery, pHostAddress); QMetaObject::invokeMethod(pConnector.data(), [ = ] { - pConnector->onConnectRequest(descr, pPassword); + pConnector->onConnectRequest(pDiscovery, pPassword); }, Qt::QueuedConnection); } void verifySuccessSignal(const QSignalSpy& pSignalSpy, - const quint16 pPort) + const QByteArray& pIfdId) { - const static QHostAddress HOST_ADDRESS(QHostAddress::LocalHost); - const static QString IFD_NAME("Smartphone1"_L1); - bool signalFound = false; for (const QList& arguments : pSignalSpy) { const QVariant remoteDeviceDescriptorVariant = arguments.at(0); - QVERIFY(remoteDeviceDescriptorVariant.canConvert()); - const IfdDescriptor descr = remoteDeviceDescriptorVariant.value(); - QVERIFY(!descr.isNull()); + QVERIFY(remoteDeviceDescriptorVariant.canConvert()); + const auto& ifdId = remoteDeviceDescriptorVariant.value(); const QVariant dispatcherVariant = arguments.at(1); QVERIFY(dispatcherVariant.canConvert>()); const QSharedPointer dispatcher = dispatcherVariant.value>(); QVERIFY(dispatcher); - const QUrl remoteUrl = descr.getUrl(); - const QString remoteAddress = remoteUrl.host(); - const int remotePort = remoteUrl.port(); - const bool signalMatches = remoteAddress == HOST_ADDRESS.toString() && remotePort == pPort && - descr.getIfdName() == IFD_NAME; - if (signalMatches) + if (ifdId == pIfdId) { qDebug() << "Success verified"; signalFound = true; @@ -118,39 +109,27 @@ void verifyErrorSignal(const QSignalSpy& pSignalSpy, const QList& pRemoteErrorCodeList, - const quint16 pPort, - const QString& pIfdName, + const QByteArray& pIfdId, const bool pExpectingNullDeviceDescriptor = false) { - const static QHostAddress HOST_ADDRESS(QHostAddress::LocalHost); - bool signalFound = false; for (const QList& arguments : pSignalSpy) { const QVariant remoteDeviceDescriptorVariant = arguments.at(0); - QVERIFY(remoteDeviceDescriptorVariant.canConvert()); - const IfdDescriptor descr = remoteDeviceDescriptorVariant.value(); + QVERIFY(remoteDeviceDescriptorVariant.canConvert()); + const auto& ifdId = remoteDeviceDescriptorVariant.value(); - if (pExpectingNullDeviceDescriptor && descr.isNull()) + if (pExpectingNullDeviceDescriptor) { qDebug() << "Error verified"; signalFound = true; break; } - QVERIFY(!descr.isNull()); const QVariant errorCodeVariant = arguments.at(1); QVERIFY(errorCodeVariant.canConvert()); const auto errorCode = errorCodeVariant.value(); - - const QUrl remoteUrl = descr.getUrl(); - const QString remoteAddress = remoteUrl.host(); - const int remotePort = remoteUrl.port(); - const bool signalMatches = remoteAddress == HOST_ADDRESS.toString() && - remotePort == pPort && - descr.getIfdName() == pIfdName && - pRemoteErrorCodeList.contains(errorCode); - if (signalMatches) + if (ifdId == pIfdId && pRemoteErrorCodeList.contains(errorCode)) { qDebug() << "Error verified"; signalFound = true; @@ -180,15 +159,16 @@ clientThread.start(); // No device name. - const QHostAddress hostAddress(QHostAddress::LocalHost); - const Discovery discoveryMsg(QString(), QByteArrayLiteral("0123456789ABCDEF"), 2020, {IfdVersion::Version::latest}); - sendRequest(connector, hostAddress, discoveryMsg, QByteArray()); + Discovery discoveryMsg(QString(), QByteArrayLiteral("0123456789ABCDEF"), 2020, {IfdVersion::Version::latest}); + discoveryMsg.setAddresses({QHostAddress(QHostAddress::LocalHost)}); + sendRequest(connector, discoveryMsg, QByteArray()); QTRY_COMPARE(spyError.count(), 1); // clazy:exclude=qstring-allocations + QCOMPARE(connector->mPendingRequests.size(), 0); clientThread.exit(); QVERIFY(clientThread.wait()); - verifyErrorSignal(spyError, {IfdErrorCode::INVALID_REQUEST}, 2020, QString()); + verifyErrorSignal(spyError, {IfdErrorCode::INVALID_REQUEST}, discoveryMsg.getIfdId()); QCOMPARE(spySuccess.count(), 0); } @@ -205,14 +185,16 @@ clientThread.start(); // Device information is null. - const QHostAddress hostAddress(QHostAddress::LocalHost); - sendRequest(connector, hostAddress, Discovery(QJsonObject()), "secret"); + Discovery discoveryMsg((QJsonObject())); + discoveryMsg.setAddresses({QHostAddress(QHostAddress::LocalHost)}); + sendRequest(connector, Discovery(QJsonObject()), "secret"); QTRY_COMPARE(spyError.count(), 1); // clazy:exclude=qstring-allocations + QCOMPARE(connector->mPendingRequests.size(), 0); clientThread.exit(); QVERIFY(clientThread.wait()); - verifyErrorSignal(spyError, {IfdErrorCode::INVALID_REQUEST}, 0, QString(), true); + verifyErrorSignal(spyError, {IfdErrorCode::INVALID_REQUEST}, discoveryMsg.getIfdId(), true); QCOMPARE(spySuccess.count(), 0); } @@ -231,15 +213,16 @@ clientThread.start(); // Password is empty. - const QHostAddress hostAddress(QHostAddress::LocalHost); - const Discovery discoveryMsg = getDiscovery(QStringLiteral("Smartphone1"), server->getServerPort()); - sendRequest(connector, hostAddress, discoveryMsg, QByteArray()); + const auto& discoveryMsg = getDiscovery(QStringLiteral("Smartphone1"), server->getServerPort()); + QTest::ignoreMessage(QtMsgType::QtDebugMsg, "Request connection."); + sendRequest(connector, discoveryMsg, QByteArray()); QTRY_COMPARE(spyError.count(), 1); // clazy:exclude=qstring-allocations + QCOMPARE(connector->mPendingRequests.size(), 0); clientThread.exit(); QVERIFY(clientThread.wait()); - verifyErrorSignal(spyError, {IfdErrorCode::REMOTE_HOST_REFUSED_CONNECTION}, server->getServerPort(), QStringLiteral("Smartphone1")); + verifyErrorSignal(spyError, {IfdErrorCode::REMOTE_HOST_REFUSED_CONNECTION}, discoveryMsg.getIfdId()); QCOMPARE(spySuccess.count(), 0); } @@ -256,15 +239,16 @@ clientThread.start(); // Currently, only API level 1 is supported. - const QHostAddress hostAddress(QHostAddress::LocalHost); - const Discovery discoveryMsg(QStringLiteral("Smartphone1"), QByteArrayLiteral("0123456789ABCDEF"), 2020, {IfdVersion::Version::Unknown}); - sendRequest(connector, hostAddress, discoveryMsg, "secret"); + Discovery discoveryMsg(QStringLiteral("Smartphone1"), QByteArrayLiteral("0123456789ABCDEF"), 2020, {IfdVersion::Version::Unknown}); + discoveryMsg.setAddresses({QHostAddress(QHostAddress::LocalHost)}); + sendRequest(connector, discoveryMsg, "secret"); QTRY_COMPARE(spyError.count(), 1); // clazy:exclude=qstring-allocations + QCOMPARE(connector->mPendingRequests.size(), 0); clientThread.exit(); QVERIFY(clientThread.wait()); - verifyErrorSignal(spyError, {IfdErrorCode::NO_SUPPORTED_API_LEVEL}, 2020, QStringLiteral("Smartphone1")); + verifyErrorSignal(spyError, {IfdErrorCode::NO_SUPPORTED_API_LEVEL}, discoveryMsg.getIfdId()); QCOMPARE(spySuccess.count(), 0); } @@ -281,20 +265,16 @@ clientThread.start(); // Correct request but no server is running. - const QHostAddress hostAddress(QHostAddress::LocalHost); - const Discovery discoveryMsg = getDiscovery(QStringLiteral("Smartphone1"), 2020); - sendRequest(connector, hostAddress, discoveryMsg, "dummy"); + const auto& discoveryMsg = getDiscovery(QStringLiteral("Smartphone1"), 2020); + QTest::ignoreMessage(QtMsgType::QtDebugMsg, "Request connection."); + sendRequest(connector, discoveryMsg, "dummy"); QTRY_COMPARE(spyError.count(), 1); // clazy:exclude=qstring-allocations + QCOMPARE(connector->mPendingRequests.size(), 0); clientThread.exit(); QVERIFY(clientThread.wait()); -#if !defined(Q_OS_FREEBSD) && !defined(Q_OS_WIN) - verifyErrorSignal(spyError, {IfdErrorCode::CONNECTION_ERROR}, 2020, QStringLiteral("Smartphone1")); -#else - verifyErrorSignal(spyError, {IfdErrorCode::CONNECTION_TIMEOUT, IfdErrorCode::CONNECTION_ERROR}, 2020, QStringLiteral("Smartphone1")); -#endif - + verifyErrorSignal(spyError, {IfdErrorCode::CONNECTION_ERROR}, discoveryMsg.getIfdId()); QCOMPARE(spySuccess.count(), 0); } @@ -359,13 +339,13 @@ clientThread.start(); // Send valid encrypted connect request. - const QHostAddress hostAddress(QHostAddress::LocalHost); - const Discovery discoveryMsg = getDiscovery(QStringLiteral("Smartphone1"), serverPort); - sendRequest(connector, hostAddress, discoveryMsg, psk); - + const auto& discoveryMsg = getDiscovery(QStringLiteral("Smartphone1"), serverPort); + QTest::ignoreMessage(QtMsgType::QtDebugMsg, "Request connection."); + sendRequest(connector, discoveryMsg, psk); QTRY_COMPARE(spyConnectorSuccess.count(), 1); // clazy:exclude=qstring-allocations QCOMPARE(spyConnectorError.count(), 0); - verifySuccessSignal(spyConnectorSuccess, serverPort); + QCOMPARE(connector->mPendingRequests.size(), 0); + verifySuccessSignal(spyConnectorSuccess, discoveryMsg.getIfdId()); const QVariant dispatcherVariant = spyConnectorSuccess.first().at(1); QVERIFY(dispatcherVariant.canConvert>()); @@ -418,14 +398,15 @@ clientThread.start(); // Send encrypted connect request with wrong psk. - const QHostAddress hostAddress(QHostAddress::LocalHost); - const Discovery discoveryMsg = getDiscovery(QStringLiteral("Smartphone1"), serverPort); - sendRequest(connector, hostAddress, discoveryMsg, "sekret"); + const auto& discoveryMsg = getDiscovery(QStringLiteral("Smartphone1"), serverPort); + QTest::ignoreMessage(QtMsgType::QtDebugMsg, "Request connection."); + sendRequest(connector, discoveryMsg, "sekret"); QTRY_COMPARE(spyConnectorError.count(), 1); // clazy:exclude=qstring-allocations QCOMPARE(spyConnectorSuccess.count(), 0); + QCOMPARE(connector->mPendingRequests.size(), 0); - verifyErrorSignal(spyConnectorError, {IfdErrorCode::REMOTE_HOST_REFUSED_CONNECTION}, serverPort, QStringLiteral("Smartphone1")); + verifyErrorSignal(spyConnectorError, {IfdErrorCode::REMOTE_HOST_REFUSED_CONNECTION}, discoveryMsg.getIfdId()); QCOMPARE(spySocketError.count(), 0); QCOMPARE(spySocketSuccess.count(), 0); diff -Nru ausweisapp2-2.3.1/test/qt/ifd/test_IfdDescriptor.cpp ausweisapp2-2.4.0/test/qt/ifd/test_IfdDescriptor.cpp --- ausweisapp2-2.3.1/test/qt/ifd/test_IfdDescriptor.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/test_IfdDescriptor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -/** - * Copyright (c) 2017-2025 Governikus GmbH & Co. KG, Germany - */ - -#include "IfdDescriptor.h" - -#include "messages/Discovery.h" - -#include - -using namespace Qt::Literals::StringLiterals; -using namespace governikus; - - -class test_IfdDescriptor - : public QObject -{ - Q_OBJECT - - private: - quint16 mPort = 0; - - private Q_SLOTS: - void initTestCase() - { - mPort = 0; - } - - - void testValidDescriptorIsEqualToItself() - { - const Discovery validMsg(QStringLiteral("Device"), QByteArrayLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::latest}); - const QHostAddress address(QHostAddress::LocalHost); - const IfdDescriptor valid(validMsg, address); - - QVERIFY(valid == valid); - } - - - void testDistinctInvalidDescriptorsAreEqual() - { - const QHostAddress address1(QStringLiteral("192.168.1.1")); - const QHostAddress address2(QHostAddress::LocalHost); - - const IfdDescriptor invalid1(Discovery(QJsonObject()), address1); - const IfdDescriptor invalid2(Discovery(QJsonObject()), address2); - - QVERIFY(invalid1 == invalid2); - } - - - void testValidDescriptorIsDifferentFromInvalid() - { - const Discovery validMsg(QStringLiteral("Device"), QByteArrayLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::latest}); - const Discovery invalidMsg(""_L1, ""_ba, 0, {}); - const QHostAddress address(QHostAddress::LocalHost); - - const IfdDescriptor valid(validMsg, address); - const IfdDescriptor invalid(invalidMsg, address); - - QVERIFY(!(valid == invalid)); - } - - - void testDistinctValidDescriptorsWithDifferentDataAreDifferent() - { - const Discovery validMsg1(QStringLiteral("Device"), QByteArrayLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::latest}); - const Discovery validMsg2(QStringLiteral("Device"), QByteArrayLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::latest, IfdVersion::Version::v2}); - const QHostAddress address(QHostAddress::LocalHost); - - const IfdDescriptor valid1(validMsg1, address); - const IfdDescriptor valid2(validMsg2, address); - - QVERIFY(!(valid1 == valid2)); - } - - - void testDistinctValidDescriptorsWithTheSameDataAreEqual() - { - const Discovery validMsg1(QStringLiteral("Device"), QByteArrayLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::latest}); - const Discovery validMsg2(QStringLiteral("Device"), QByteArrayLiteral("0123456789ABCDEF"), mPort, {IfdVersion::Version::latest}); - const QHostAddress address(QHostAddress::LocalHost); - - const IfdDescriptor valid1(validMsg1, address); - const IfdDescriptor valid2(validMsg2, address); - - QVERIFY(valid1 == valid2); - } - - -}; - -QTEST_GUILESS_MAIN(test_IfdDescriptor) -#include "test_IfdDescriptor.moc" diff -Nru ausweisapp2-2.3.1/test/qt/ifd/test_IfdListImpl.cpp ausweisapp2-2.4.0/test/qt/ifd/test_IfdListImpl.cpp --- ausweisapp2-2.3.1/test/qt/ifd/test_IfdListImpl.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/test_IfdListImpl.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -8,6 +8,7 @@ #include + using namespace Qt::Literals::StringLiterals; using namespace governikus; @@ -29,42 +30,47 @@ const QHostAddress addr2 = QHostAddress("5.6.7.9"_L1); { - const IfdDescriptor descr(offerMsg1, addr1); + auto discovery = offerMsg1; + discovery.setAddresses({addr1}); QSignalSpy spy(&deviceList, &IfdListImpl::fireDeviceAppeared); - deviceList.update(descr); + deviceList.update(discovery); QCOMPARE(spy.count(), 1); } { - const IfdDescriptor descr(offerMsg1, addr1); + auto discovery = offerMsg1; + discovery.setAddresses({addr1}); QSignalSpy spy(&deviceList, &IfdListImpl::fireDeviceAppeared); - deviceList.update(descr); + deviceList.update(discovery); QCOMPARE(spy.count(), 0); } { - const IfdDescriptor descr(offerMsg1, addr2); + auto discovery = offerMsg1; + discovery.setAddresses({addr2}); QSignalSpy spy(&deviceList, &IfdListImpl::fireDeviceAppeared); - deviceList.update(descr); + deviceList.update(discovery); QCOMPARE(spy.count(), 0); } { - const IfdDescriptor descr(offerMsg2, addr2); + auto discovery = offerMsg2; + discovery.setAddresses({addr2}); QSignalSpy spy(&deviceList, &IfdListImpl::fireDeviceAppeared); - deviceList.update(descr); + deviceList.update(discovery); QCOMPARE(spy.count(), 1); } { - const IfdDescriptor descr(offerMsg2, addr1); + auto discovery = offerMsg2; + discovery.setAddresses({addr1}); QSignalSpy spy(&deviceList, &IfdListImpl::fireDeviceAppeared); - deviceList.update(descr); + deviceList.update(discovery); QCOMPARE(spy.count(), 0); } diff -Nru ausweisapp2-2.3.1/test/qt/ifd/test_ServerMessageHandler.cpp ausweisapp2-2.4.0/test/qt/ifd/test_ServerMessageHandler.cpp --- ausweisapp2-2.3.1/test/qt/ifd/test_ServerMessageHandler.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ifd/test_ServerMessageHandler.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -149,7 +149,7 @@ ServerMessageHandlerImpl serverMessageHandler(mDataChannel); IfdConnectResponse unexpectedMsg(QStringLiteral("RemoteReader")); - mDataChannel->onReceived(unexpectedMsg.toByteArray(IfdVersion::Version::latest, QStringLiteral("invalidConextHandle"))); + mDataChannel->onReceived(unexpectedMsg.toByteArray(IfdVersion::Version::latest, QStringLiteral("invalidContextHandle"))); QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Invalid context handle received"))); } @@ -207,7 +207,7 @@ IfdConnectResponse("NFC Reader"_L1).toByteArray(IfdVersion::Version::v2, contextHandle), IfdDisconnectResponse("NFC Reader"_L1).toByteArray(IfdVersion::Version::v2, contextHandle), IfdTransmitResponse("NFC Reader"_L1, "9000").toByteArray(IfdVersion::Version::v2, contextHandle), - IfdEstablishPaceChannelResponse("My little Reader"_L1, EstablishPaceChannelOutput()).toByteArray(IfdVersion::Version::v2, contextHandle) + IfdEstablishPaceChannelResponse("My little Reader"_L1, EstablishPaceChannelOutput(), ECardApiResult::Minor::null).toByteArray(IfdVersion::Version::v2, contextHandle) }); for (const auto& serverMessage : serverMessages) { @@ -298,7 +298,7 @@ QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations reader->setCard(MockCardConfig()); QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations @@ -354,13 +354,14 @@ void ifdDisconnectForReaderWithConnectedCardSendsCorrectResponse() { + QSignalSpy logSpy(Env::getSingleton()->getEventHandler(), &LogEventHandler::fireLog); ServerMessageHandlerImpl serverMessageHandler(mDataChannel); QString contextHandle; ensureContext(contextHandle); QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations reader->setCard(MockCardConfig()); QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations @@ -409,6 +410,10 @@ QCOMPARE(disconnectResponse.getResultMinor(), ECardApiResult::Minor::null); removeReaderAndConsumeMessages(QStringLiteral("test-reader")); + + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Update retry counter before disconnect card for \"{"))); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Update retry counter for \"{"))); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("}\" finished with COMMAND_FAILED"))); } @@ -421,7 +426,7 @@ QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations reader->setCard(MockCardConfig()); QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations @@ -484,7 +489,7 @@ QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations reader->setCard(MockCardConfig()); QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations @@ -547,7 +552,7 @@ QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations reader->setCard(MockCardConfig()); QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations @@ -622,7 +627,7 @@ QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations reader->setCard(MockCardConfig({ {CardReturnCode::OK, QByteArray("9000")} @@ -690,7 +695,7 @@ QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations reader->setCard(MockCardConfig()); QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations @@ -757,7 +762,7 @@ QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations reader->setCard(MockCardConfig({ {CardReturnCode::OK, QByteArray("6700")} @@ -833,7 +838,7 @@ QSignalSpy sendSpy(mDataChannel.data(), &MockDataChannel::fireSend); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("test-reader"_L1); QTRY_COMPARE(sendSpy.count(), 1); // clazy:exclude=qstring-allocations reader->setCard(MockCardConfig()); QTRY_COMPARE(sendSpy.count(), 2); // clazy:exclude=qstring-allocations @@ -928,8 +933,10 @@ QTest::addColumn("returnCode"); QTest::addColumn("minor"); + QTest::newRow("timeout") << CardReturnCode::INPUT_TIME_OUT << ECardApiResult::Minor::IFDL_Timeout_Error; + QTest::newRow("cancel") << CardReturnCode::CANCELLATION_BY_USER << ECardApiResult::Minor::IFDL_CancellationByUser; + QTest::newRow("noCard") << CardReturnCode::CARD_NOT_FOUND << ECardApiResult::Minor::IFDL_Terminal_NoCard; 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; } diff -Nru ausweisapp2-2.3.1/test/qt/network/test_DatagramHandlerImpl.cpp ausweisapp2-2.4.0/test/qt/network/test_DatagramHandlerImpl.cpp --- ausweisapp2-2.3.1/test/qt/network/test_DatagramHandlerImpl.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/network/test_DatagramHandlerImpl.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -18,6 +18,7 @@ #include #endif + using namespace Qt::Literals::StringLiterals; using namespace governikus; @@ -52,13 +53,13 @@ QSharedPointer socket(Env::create()); QVERIFY(socket->isBound()); - QCOMPARE(logSpy.count(), 1); - auto param = logSpy.takeFirst(); + QVERIFY(!logSpy.isEmpty()); + auto param = logSpy.takeLast(); QVERIFY(param.at(0).toString().contains("Bound on port:"_L1)); socket.reset(); - QCOMPARE(logSpy.count(), 1); - param = logSpy.takeFirst(); + QVERIFY(!logSpy.isEmpty()); + param = logSpy.takeLast(); QVERIFY(param.at(0).toString().contains("Shutdown socket"_L1)); } @@ -127,35 +128,63 @@ } #endif - QSharedPointer socket(Env::create()); - QVERIFY(socket->isBound()); - QSignalSpy spySocket(socket.data(), &DatagramHandler::fireNewMessage); + DatagramHandlerImpl socket; + QVERIFY(socket.isBound()); + QSignalSpy spySocket(&socket, &DatagramHandler::fireNewMessage); QUdpSocket clientSocket; clientSocket.setProxy(QNetworkProxy::NoProxy); - QByteArray data(R"({"key":"value"})"); - auto written = clientSocket.writeDatagram(data, broadcast ? QHostAddress::Broadcast : QHostAddress::LocalHost, socket.staticCast()->mSocket->localPort()); - QTRY_COMPARE(spySocket.count(), 1); // clazy:exclude=qstring-allocations - QCOMPARE(written, data.size()); - const auto& msg = spySocket.takeFirst(); - QCOMPARE(msg.size(), 2); - QCOMPARE(msg.at(0).toByteArray(), data); + QList addresses; + if (broadcast) + { + const auto entries = socket.getAllBroadcastEntries(); + for (const auto& entry : entries) + { + const auto& addr = socket.getBroadcastAddress(entry); + if (!addr.isNull()) + { + addresses << addr; + } + } + } + else + { + addresses << QHostAddress::LocalHost; + } + + for (const auto& address : std::as_const(addresses)) + { + QByteArray data(R"({"key":"value"})"); + const auto written = clientSocket.writeDatagram(data, address, socket.mSocket->localPort()); + if (written == -1) + { + qCritical() << address << clientSocket.error() << clientSocket.errorString(); + } + QTRY_COMPARE(spySocket.count(), 1); // clazy:exclude=qstring-allocations + QCOMPARE(written, data.size()); + const auto& msg = spySocket.takeFirst(); + QCOMPARE(msg.size(), 2); + QCOMPARE(msg.at(0).toByteArray(), data); + } } void sendDatagram_data() { QTest::addColumn("broadcast"); + QTest::addColumn("duplicate"); - QTest::newRow("WithBroadcast") << true; - QTest::newRow("WithoutBroadcast") << false; + QTest::newRow("WithBroadcast - Unique") << true << false; + QTest::newRow("WithBroadcast - Duplicate") << true << true; + QTest::newRow("WithoutBroadcast") << false << false; } void sendDatagram() { QFETCH(bool, broadcast); + QFETCH(bool, duplicate); QUdpSocket receiver; receiver.setProxy(QNetworkProxy::NoProxy); @@ -175,7 +204,12 @@ #ifdef Q_OS_FREEBSD QSKIP("FreeBSD does not like that"); #endif - datagramHandlerImpl->sendToAllAddressEntries(doc.toJson(QJsonDocument::Compact), receiver.localPort()); + auto entries = datagramHandlerImpl->getAllBroadcastEntries(); + if (duplicate) + { + entries << entries.last(); + } + datagramHandlerImpl->sendToAddressEntries(doc.toJson(QJsonDocument::Compact), entries, receiver.localPort()); } else { @@ -183,7 +217,19 @@ } QTRY_COMPARE(spyReceiver.count(), 1); // clazy:exclude=qstring-allocations - QCOMPARE(logSpy.count(), broadcast ? QNetworkInterface::allInterfaces().size() + 1 : 0); + if (broadcast) + { + QVERIFY(logSpy.count() >= (duplicate ? 4 : 2)); + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Broadcast Addresses changed..."))); + if (duplicate) + { + QVERIFY(TestFileHelper::containsLog(logSpy, QLatin1String("Skipping duplicate broadcasting to"))); + } + } + else + { + QVERIFY(logSpy.isEmpty()); + } QVERIFY(receiver.hasPendingDatagrams()); QByteArray msg; diff -Nru ausweisapp2-2.3.1/test/qt/network/test_NetworkManager.cpp ausweisapp2-2.4.0/test/qt/network/test_NetworkManager.cpp --- ausweisapp2-2.3.1/test/qt/network/test_NetworkManager.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/network/test_NetworkManager.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -15,6 +15,7 @@ #include "MockNetworkManager.h" #include "MockNetworkReply.h" +#include #include #include #include @@ -125,6 +126,47 @@ } + void test_onSslErrors_OcspNoResponseFound() + { + auto sessionTicket = QByteArray("sessionTicket"); + auto networkManager = Env::getSingleton(); + auto reply = QSharedPointer::create(); + auto spy = QSignalSpy(reply.data(), &QNetworkReply::finished); + auto sslcfg = QSslConfiguration(); + + sslcfg.setSessionTicket(sessionTicket); + networkManager->mUpdaterSessions.insert(sessionTicket); + reply->setSslConfigurationImplementation(sslcfg); + networkManager->onSslErrors(reply, QList(1, {QSslError(QSslError::OcspNoResponseFound)})); + QCOMPARE(spy.count(), 0); + } + + + void test_onSslErrors_CertificateBlacklisted() + { + auto networkManager = Env::getSingleton(); + auto reply = QSharedPointer::create(); + auto spy = QSignalSpy(reply.data(), &QNetworkReply::finished); + auto sslcfg = QSslConfiguration(); + + reply->setSslConfigurationImplementation(sslcfg); + QTest::ignoreMessage(QtCriticalMsg, QRegularExpression(QStringLiteral("Fatal SSL error: .*"))); + networkManager->onSslErrors(reply, QList(1, {QSslError(QSslError::CertificateBlacklisted)})); + QCOMPARE(spy.count(), 1); + } + + + void test_onEncryptedResponse() + { + auto networkManager = Env::getSingleton(); + auto reply = QSharedPointer::create(); + auto spy = QSignalSpy(reply.data(), &QNetworkReply::finished); + QTest::ignoreMessage(QtCriticalMsg, QRegularExpression(QStringLiteral("Untrusted certificate found .*"))); + networkManager->onEncryptedResponse(reply); + QCOMPARE(spy.count(), 1); + } + + void serviceUnavailableWorkflow() { MockNetworkManager networkManager; @@ -134,6 +176,8 @@ }, Qt::QueuedConnection); auto* reply = new MockNetworkReply(); + const auto url = reply->url().toString(); + reply->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, QVariant(503)); reply->setError(QNetworkReply::ServiceUnavailableError, "dummy"_L1); networkManager.setNextReply(reply); @@ -148,7 +192,11 @@ controller.run(); QTRY_COMPARE(spy.count(), 1); // clazy:exclude=qstring-allocations - QCOMPARE(context->getStatus(), GlobalStatus(GlobalStatus::Code::Workflow_TrustedChannel_ServiceUnavailable, {GlobalStatus::ExternalInformation::LAST_URL, QString()})); + const GlobalStatus::ExternalInfoMap infoMap { + {GlobalStatus::ExternalInformation::HTTP_STATUS_CODE, QString::number(503)}, + {GlobalStatus::ExternalInformation::LAST_URL, url} + }; + QCOMPARE(context->getStatus(), GlobalStatus(GlobalStatus::Code::Workflow_TrustedChannel_ServiceUnavailable, infoMap)); } diff -Nru ausweisapp2-2.3.1/test/qt/network/test_TlsChecker.cpp ausweisapp2-2.4.0/test/qt/network/test_TlsChecker.cpp --- ausweisapp2-2.3.1/test/qt/network/test_TlsChecker.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/network/test_TlsChecker.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -383,17 +383,9 @@ QCOMPARE(logSpy.count(), 6); QVERIFY(logSpy.at(0).at(0).toString().contains("Used session cipher QSslCipher(name=, bits=0, proto=)"_L1)); -#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0)) - QVERIFY(logSpy.at(1).at(0).toString().contains("Used session protocol: \"UnknownProtocol\""_L1)); -#else QVERIFY(logSpy.at(1).at(0).toString().contains("Used session protocol: QSsl::UnknownProtocol"_L1)); -#endif QVERIFY(logSpy.at(2).at(0).toString().contains("Used ephemeral server key:"_L1)); -#if (QT_VERSION < QT_VERSION_CHECK(6, 5, 0)) - QVERIFY(logSpy.at(3).at(0).toString().contains("Used peer certificate: QSslCertificate(\"\", \"\", \"1B2M2Y8AsgTpgAmY7PhCfg==\""_L1)); -#else QVERIFY(logSpy.at(3).at(0).toString().contains(R"(Used peer certificate: QSslCertificate(Version="", SerialNumber="", Digest="1B2M2Y8AsgTpgAmY7PhCfg==", Issuer="", Subject="", AlternativeSubjectNames=QMultiMap(), EffectiveDate=QDateTime(Invalid), ExpiryDate=QDateTime(Invalid))"_L1)); -#endif QVERIFY(logSpy.at(4).at(0).toString().contains("Used ssl session: \"\""_L1)); QVERIFY(logSpy.at(5).at(0).toString().contains("Handshake of tls connection done!"_L1)); } diff -Nru ausweisapp2-2.3.1/test/qt/network/test_UrlUtil.cpp ausweisapp2-2.4.0/test/qt/network/test_UrlUtil.cpp --- ausweisapp2-2.3.1/test/qt/network/test_UrlUtil.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/network/test_UrlUtil.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -7,6 +7,8 @@ #include "AppSettings.h" #include "Env.h" +#include "MockNetworkReply.h" + #include using namespace Qt::Literals::StringLiterals; @@ -129,6 +131,80 @@ } + void resolveRedirect_data() + { + QTest::addColumn("requestUrl"); + QTest::addColumn("responseUrl"); + QTest::addColumn("resultUrl"); + + QLatin1String emptyUrl; + QLatin1String invalidUrl("://://"); + + QLatin1String requestUrl1("http://www.foo.bar"); + QLatin1String requestUrl2("http://www.foo.bar/"); + QLatin1String requestUrl3("http://www.foo.bar/world.html"); + QLatin1String requestUrl4("http://www.foo.bar/hello/world.html"); + QLatin1String requestUrl5("http://www.foo.bar/hello/again/world.html"); + + QLatin1String responseUrl1("http://www.bar.foo/index.html"); + QLatin1String responseUrl2("index.html"); + QLatin1String responseUrl3("/index.html"); + QLatin1String responseUrl4("./index.html"); + QLatin1String responseUrl5("../index.html"); + + QLatin1String resultUrl1("http://www.bar.foo/index.html"); + QLatin1String resultUrl2("http://www.foo.bar/index.html"); + QLatin1String resultUrl3("http://www.foo.bar/hello/index.html"); + QLatin1String resultUrl4("http://www.foo.bar/hello/again/index.html"); + + QTest::newRow("e1") << requestUrl1 << emptyUrl << emptyUrl; + QTest::newRow("e2") << requestUrl1 << invalidUrl << invalidUrl; + + QTest::newRow("01") << requestUrl1 << responseUrl1 << resultUrl1; + QTest::newRow("02") << requestUrl1 << responseUrl2 << resultUrl2; + QTest::newRow("03") << requestUrl1 << responseUrl3 << resultUrl2; + QTest::newRow("04") << requestUrl1 << responseUrl4 << resultUrl2; + QTest::newRow("05") << requestUrl1 << responseUrl5 << resultUrl2; + + QTest::newRow("06") << requestUrl2 << responseUrl1 << resultUrl1; + QTest::newRow("07") << requestUrl2 << responseUrl2 << resultUrl2; + QTest::newRow("08") << requestUrl2 << responseUrl3 << resultUrl2; + QTest::newRow("09") << requestUrl2 << responseUrl4 << resultUrl2; + QTest::newRow("10") << requestUrl2 << responseUrl5 << resultUrl2; + + QTest::newRow("11") << requestUrl3 << responseUrl1 << resultUrl1; + QTest::newRow("12") << requestUrl3 << responseUrl2 << resultUrl2; + QTest::newRow("13") << requestUrl3 << responseUrl3 << resultUrl2; + QTest::newRow("14") << requestUrl3 << responseUrl4 << resultUrl2; + QTest::newRow("15") << requestUrl3 << responseUrl5 << resultUrl2; + + QTest::newRow("16") << requestUrl4 << responseUrl1 << resultUrl1; + QTest::newRow("17") << requestUrl4 << responseUrl2 << resultUrl3; + QTest::newRow("18") << requestUrl4 << responseUrl3 << resultUrl2; + QTest::newRow("19") << requestUrl4 << responseUrl4 << resultUrl3; + QTest::newRow("20") << requestUrl4 << responseUrl5 << resultUrl2; + + QTest::newRow("21") << requestUrl5 << responseUrl1 << resultUrl1; + QTest::newRow("22") << requestUrl5 << responseUrl2 << resultUrl4; + QTest::newRow("23") << requestUrl5 << responseUrl3 << resultUrl2; + QTest::newRow("24") << requestUrl5 << responseUrl4 << resultUrl4; + QTest::newRow("25") << requestUrl5 << responseUrl5 << resultUrl3; + } + + + void resolveRedirect() + { + QFETCH(QLatin1String, requestUrl); + QFETCH(QLatin1String, responseUrl); + QFETCH(QLatin1String, resultUrl); + + const auto& reply = QSharedPointer::create(); + reply->setRequest(QNetworkRequest(QUrl(requestUrl))); + reply->setAttribute(QNetworkRequest::RedirectionTargetAttribute, responseUrl); + QCOMPARE(UrlUtil::resolveRedirect(reply), QUrl(resultUrl)); + } + + }; QTEST_GUILESS_MAIN(test_UrlUtil) diff -Nru ausweisapp2-2.3.1/test/qt/services/test_AppUpdatData.cpp ausweisapp2-2.4.0/test/qt/services/test_AppUpdatData.cpp --- ausweisapp2-2.3.1/test/qt/services/test_AppUpdatData.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/services/test_AppUpdatData.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -62,17 +62,6 @@ } - void test_notes() - { - AppUpdateData data; - QCOMPARE(data.getNotes(), QString()); - - data.setNotes(QStringLiteral("Release Notes")); - QCOMPARE(data.getNotes(), QStringLiteral("Release Notes")); - QCOMPARE(data.isValid(), false); - } - - void test_parsing() { const QByteArray jsonData(R"({)" @@ -85,8 +74,7 @@ R"( "version": "1.22.0",)" R"( "url": "https://www.example.com/aa2.zip",)" R"( "size": 1337,)" - R"( "checksum": "https://www.example.com/aa2.zip.sha256",)" - R"( "notes": "https://www.example.com/ReleaseNotes.html#1.22.0")" + R"( "checksum": "https://www.example.com/aa2.zip.sha256")" R"( })" R"(, {)" R"( "date": "2017-10-25T15:20:25",)" @@ -95,8 +83,7 @@ R"( "version": "1.22.0",)" R"( "url": "https://www.example.com/aa2.zip",)" R"( "size": 1337,)" - R"( "checksum": "https://www.example.com/aa2.zip.sha256",)" - R"( "notes": "https://www.example.com/ReleaseNotes.html#1.22.0")" + R"( "checksum": "https://www.example.com/aa2.zip.sha256")" R"( })" R"(, {)" R"( "date": "2017-10-25T15:20:25",)" @@ -105,8 +92,7 @@ R"( "version": "1.22.0",)" R"( "url": "https://www.example.com/aa2.zip",)" R"( "size": 1337,)" - R"( "checksum": "https://www.example.com/aa2.zip.sha256",)" - R"( "notes": "https://www.example.com/ReleaseNotes.html#1.22.0")" + R"( "checksum": "https://www.example.com/aa2.zip.sha256")" R"( })" R"( ])" R"(})"); @@ -120,8 +106,6 @@ QCOMPARE(data.getUrl(), QUrl(QStringLiteral("https://www.example.com/aa2.zip"))); QCOMPARE(data.getSize(), 1337); QCOMPARE(data.getChecksumUrl(), QUrl(QStringLiteral("https://www.example.com/aa2.zip.sha256"))); - QCOMPARE(data.getNotesUrl(), QUrl(QStringLiteral("https://www.example.com/ReleaseNotes.html#1.22.0"))); - QCOMPARE(data.getNotes(), QString()); } @@ -137,8 +121,7 @@ R"( "version": "1.22.0",)" R"( "url": "https://www.example.com/aa2.zip",)" R"( "size": 1337,)" - R"( "checksum": "https://www.example.com/aa2.zip.sha256",)" - R"( "notes": "https://www.example.com/ReleaseNotes.html#1.22.0")" + R"( "checksum": "https://www.example.com/aa2.zip.sha256")" R"( })" R"(, {)" R"( "date": "2017-10-25T15:20:25",)" @@ -147,8 +130,7 @@ R"( "version": "1.22.0",)" R"( "url": "https://www.example.com/aa2.zip",)" R"( "size": 1337,)" - R"( "checksum": "https://www.example.com/aa2.zip.sha256",)" - R"( "notes": "https://www.example.com/ReleaseNotes.html#1.22.0")" + R"( "checksum": "https://www.example.com/aa2.zip.sha256")" R"( })" R"(, {)" R"( "date": "2017-10-25T15:20:25",)" @@ -157,8 +139,7 @@ R"( "version": "1.22.0",)" R"( "url": "https://www.example.com/aa2.zip",)" R"( "size": 1337,)" - R"( "checksum": "https://www.example.com/aa2.zip.sha256",)" - R"( "notes": "https://www.example.com/ReleaseNotes.html#1.22.0")" + R"( "checksum": "https://www.example.com/aa2.zip.sha256")" R"( })" R"( ])" R"(})"); @@ -188,8 +169,7 @@ R"( "version": "1.22.0",)" R"( "url": "https://www.example.com/aa2.zip",)" R"( "size": 1337,)" - R"( "checksum": "https://www.example.com/aa2.zip.sha256",)" - R"( "notes": "https://www.example.com/ReleaseNotes.html#1.22.0")" + R"( "checksum": "https://www.example.com/aa2.zip.sha256")" R"(})") << GlobalStatus::Code::Downloader_Data_Corrupted; QTest::newRow("corrupted5") << QByteArray(R"({)" R"( "items":)" @@ -199,8 +179,7 @@ R"( "version": "1.22.0",)" R"( "url": "https://www.example.com/aa2.zip",)" R"( "size": 1337,)" - R"( "checksum": "https://www.example.com/aa2.zip.sha256",)" - R"( "notes": "https://www.example.com/ReleaseNotes.html#1.22.0")" + R"( "checksum": "https://www.example.com/aa2.zip.sha256")" R"( })" R"(})") << GlobalStatus::Code::Downloader_Data_Corrupted; QTest::newRow("corrupted6") << QByteArray(R"({)" @@ -217,8 +196,7 @@ R"( "version": "1.22.0",)" R"( "url": "https://www.example.com/aa2.zip",)" R"( "size": 1337,)" - R"( "checksum": "https://www.example.com/aa2.zip.sha256",)" - R"( "notes": "https://www.example.com/ReleaseNotes.html#1.22.0")" + R"( "checksum": "https://www.example.com/aa2.zip.sha256")" R"( })" R"( ])" R"(})") << GlobalStatus::Code::Downloader_Missing_Platform; diff -Nru ausweisapp2-2.3.1/test/qt/services/test_AppUpdatr.cpp ausweisapp2-2.4.0/test/qt/services/test_AppUpdatr.cpp --- ausweisapp2-2.3.1/test/qt/services/test_AppUpdatr.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/services/test_AppUpdatr.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -106,9 +106,9 @@ void setJsonItemField(QJsonDocument& pDocument, const QString& pField, const QString& pValue) { - auto itemArray = pDocument.object()["items"_L1].toArray(); + auto itemArray = pDocument.object().value("items"_L1).toArray(); int i = 0; - for (auto item : itemArray) + for (auto item : std::as_const(itemArray)) { QJsonObject itemObject = item.toObject(); itemObject[pField] = pValue; @@ -135,7 +135,7 @@ const auto platform = "src"_L1; #endif - auto itemArray = pDocument.object()["items"_L1].toArray(); + const auto itemArray = pDocument.object().value("items"_L1).toArray(); for (auto item : itemArray) { QJsonObject itemObject = item.toObject(); @@ -152,9 +152,10 @@ { Env::set(Downloader::staticMetaObject, &mDownloader); - mAppCastLocation = mAppUpdater.mAppUpdateJsonUrl; + mAppCastLocation = QUrl(QStringLiteral("http://valid.url.example.com")); + mAppUpdater.setAppUpdateJsonUrl(mAppCastLocation); mJsonDocument = QJsonDocument::fromJson(test_jsonData); - mReleaseNoteLocation = getJsonItemField(mJsonDocument, "notes"_L1).toString(); + mReleaseNoteLocation = QUrl(getJsonItemField(mJsonDocument, "notes"_L1).toString()); } @@ -164,10 +165,10 @@ mDownloader.setTestData(mAppCastLocation, test_jsonData); mDownloader.setTestData(mReleaseNoteLocation, test_releaseNotes); - mAppPackage = getJsonItemField(mJsonDocument, "url"_L1).toString(); + mAppPackage = QUrl(getJsonItemField(mJsonDocument, "url"_L1).toString()); mDownloader.setTestData(mAppPackage, test_package); - mChecksum = getJsonItemField(mJsonDocument, "checksum"_L1).toString(); + mChecksum = QUrl(getJsonItemField(mJsonDocument, "checksum"_L1).toString()); QByteArray checksum(test_checksum); checksum += mAppPackage.fileName().toUtf8(); mDownloader.setTestData(mChecksum, checksum); @@ -262,7 +263,7 @@ QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Data written to file: .*"_L1)); QTest::ignoreMessage(QtDebugMsg, "Verify checksum with algorithm: QCryptographicHash::Sha256"); QTest::ignoreMessage(QtDebugMsg, QRegularExpression("Package already exists: .*"_L1)); - QTest::ignoreMessage(QtDebugMsg, "Re-use valid package..."); + QTest::ignoreMessage(QtDebugMsg, "Reuse valid package..."); QVERIFY(mAppUpdater.downloadUpdate()); checkDlResult(spyDownload, GlobalStatus::Code::No_Error); @@ -301,11 +302,9 @@ QCOMPARE(updateData.getDate(), QDateTime::fromString(getJsonItemField(document, "date"_L1).toString(), Qt::ISODate)); QCOMPARE(updateData.getVersion(), getJsonItemField(document, "version"_L1).toString()); - QCOMPARE(updateData.getNotesUrl(), QUrl(getJsonItemField(document, "notes"_L1).toString())); QCOMPARE(updateData.getUrl(), QUrl(getJsonItemField(document, "url"_L1).toString())); QCOMPARE(updateData.getChecksumUrl(), QUrl(getJsonItemField(document, "checksum"_L1).toString())); QCOMPARE(updateData.getSize(), getJsonItemField(document, "size"_L1).toInt()); - QVERIFY(mAppUpdater.getUpdateData().getNotes() != QString()); } @@ -361,21 +360,6 @@ } - void testReleaseNoteDownloadFailed() - { - QJsonDocument document = QJsonDocument::fromJson(test_jsonData); - setJsonItemField(document, "notes"_L1, "httb://notarealurl.org"_L1); - mDownloader.setTestData(mAppCastLocation, document.toJson()); - - QSignalSpy spy(&mAppUpdater, &AppUpdater::fireAppcastCheckFinished); - - QVERIFY(mAppUpdater.checkAppUpdate()); - - checkResult(spy, true, GlobalStatus::Code::Downloader_File_Not_Found); - QCOMPARE(mAppUpdater.getUpdateData().getNotes(), QString()); - } - - void testCheckSumDownloadFailed() { QJsonDocument document = QJsonDocument::fromJson(test_jsonData); diff -Nru ausweisapp2-2.3.1/test/qt/settings/test_GeneralSettings.cpp ausweisapp2-2.4.0/test/qt/settings/test_GeneralSettings.cpp --- ausweisapp2-2.3.1/test/qt/settings/test_GeneralSettings.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/settings/test_GeneralSettings.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -396,6 +396,24 @@ } + void testRemindUserOfAutoRedirect() + { + auto& settings = Env::getSingleton()->getGeneralSettings(); + + QSignalSpy spy(&settings, &GeneralSettings::fireSettingsChanged); + bool initial = settings.isRemindUserOfAutoRedirect(); + bool newValue = !initial; + + settings.setRemindUserOfAutoRedirect(newValue); + QCOMPARE(spy.count(), 1); + QCOMPARE(settings.isRemindUserOfAutoRedirect(), newValue); + + settings.setRemindUserOfAutoRedirect(initial); + QCOMPARE(spy.count(), 2); + QCOMPARE(settings.isRemindUserOfAutoRedirect(), initial); + } + + void testPersistentSettingsVersion() { auto& settings = Env::getSingleton()->getGeneralSettings(); diff -Nru ausweisapp2-2.3.1/test/qt/ui/automatic/test_UiPluginAutomatic.cpp ausweisapp2-2.4.0/test/qt/ui/automatic/test_UiPluginAutomatic.cpp --- ausweisapp2-2.3.1/test/qt/ui/automatic/test_UiPluginAutomatic.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/automatic/test_UiPluginAutomatic.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -13,19 +13,21 @@ #include "MockCardConnection.h" #include "MockReaderManagerPlugin.h" #include "TestAuthContext.h" -#include "TestWorkflowContext.h" #include "TestWorkflowController.h" #include #include #include + using namespace Qt::Literals::StringLiterals; using namespace governikus; + Q_DECLARE_METATYPE(QLatin1String) Q_IMPORT_PLUGIN(MockReaderManagerPlugin) + class DummyUI : public UiPlugin { @@ -215,7 +217,7 @@ void insertCard() { MockReaderManagerPlugin::getInstance().setInitialScanState(ReaderManagerPluginInfo::InitialScan::SUCCEEDED); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1); reader->setCard(MockCardConfig()); QVERIFY(reader->getReaderInfo().hasEid()); QVERIFY(!reader->getReaderInfo().isInsertable()); @@ -276,8 +278,8 @@ MockReaderManagerPlugin::getInstance().setPluginInfo(info); MockReaderManagerPlugin::getInstance().setInitialScanState(ReaderManagerPluginInfo::InitialScan::SUCCEEDED); - MockReader* unusedReader = MockReaderManagerPlugin::getInstance().addReader("MockReader1"_L1, readerType); - auto* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader2"_L1, readerType); + const auto& unusedReader = MockReaderManagerPlugin::getInstance().addReader("MockReader1"_L1, readerType); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader2"_L1, readerType); reader->setCard(MockCardConfig()); QSignalSpy spy(Env::getSingleton(), &ReaderManager::fireReaderPropertiesUpdated); @@ -353,7 +355,7 @@ { QFETCH(CardReturnCode, returnCode); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1, ReaderManagerPluginType::SIMULATOR); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1, ReaderManagerPluginType::SIMULATOR); reader->setCard(MockCardConfig()); reader->setInfoBasicReader(false); @@ -372,7 +374,7 @@ void handlePasswordNonBasic() { - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1); reader->setCard(MockCardConfig()); reader->setInfoBasicReader(false); @@ -446,7 +448,7 @@ qputenv(envVariable.data(), envValue); } - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1); reader->setCard(MockCardConfig()); reader->setInfoBasicReader(true); diff -Nru ausweisapp2-2.3.1/test/qt/ui/json/test_Message.cpp ausweisapp2-2.4.0/test/qt/ui/json/test_Message.cpp --- ausweisapp2-2.3.1/test/qt/ui/json/test_Message.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/json/test_Message.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -88,7 +88,7 @@ } - void noRequestWithUnknowCommand() + void noRequestWithUnknownCommand() { QByteArray msg(R"({"cmd": "UnknownRequestedCommand123"})"); MessageDispatcher dispatcher; diff -Nru ausweisapp2-2.3.1/test/qt/ui/json/test_MsgHandlerInsertCard.cpp ausweisapp2-2.4.0/test/qt/ui/json/test_MsgHandlerInsertCard.cpp --- ausweisapp2-2.3.1/test/qt/ui/json/test_MsgHandlerInsertCard.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/json/test_MsgHandlerInsertCard.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -15,11 +15,14 @@ #include #include + Q_IMPORT_PLUGIN(MockReaderManagerPlugin) + using namespace Qt::Literals::StringLiterals; using namespace governikus; + class test_MsgHandlerInsertCard : public QObject { @@ -92,7 +95,7 @@ void readerWithCard() { MockReaderManagerPlugin::getInstance().addReader("MockReader 1"_L1); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader CARD"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader CARD"_L1); reader->setCard(MockCardConfig()); MessageDispatcher dispatcher; @@ -128,7 +131,7 @@ msg = R"({"cmd": "SET_CARD", "name": "dummy"})"; QCOMPARE(dispatcher.processCommand(msg), QByteArray(R"({"error":"Unknown reader name","msg":"INSERT_CARD"})")); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReaderSmart"_L1, ReaderManagerPluginType::SMART); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReaderSmart"_L1, ReaderManagerPluginType::SMART); msg = R"({"cmd": "SET_CARD", "name": "MockReaderSmart"})"; QCOMPARE(dispatcher.processCommand(msg), QByteArray(R"({"error":"Card is not insertable","msg":"INSERT_CARD"})")); @@ -148,7 +151,7 @@ setContext(dispatcher); QCOMPARE(dispatcher.processStateChange(StateBuilder::generateStateName()), QByteArray(R"({"msg":"INSERT_CARD"})")); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReaderSimulator"_L1, ReaderManagerPluginType::SIMULATOR); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReaderSimulator"_L1, ReaderManagerPluginType::SIMULATOR); auto info = reader->getReaderInfo(); info.setCardInfo(CardInfo(CardType::EID_CARD)); info.shelveCard(); diff -Nru ausweisapp2-2.3.1/test/qt/ui/json/test_MsgHandlerReader.cpp ausweisapp2-2.4.0/test/qt/ui/json/test_MsgHandlerReader.cpp --- ausweisapp2-2.3.1/test/qt/ui/json/test_MsgHandlerReader.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/json/test_MsgHandlerReader.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -11,13 +11,17 @@ #include + Q_IMPORT_PLUGIN(MockReaderManagerPlugin) + using namespace Qt::Literals::StringLiterals; using namespace governikus; + Q_DECLARE_METATYPE(ReaderInfo) + class test_MsgHandlerReader : public QObject { @@ -90,7 +94,7 @@ void oneReaderWithCard() { - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); reader->setCard(MockCardConfig()); MessageDispatcher dispatcher; @@ -112,7 +116,7 @@ { QFETCH(MsgLevel, msgLevel); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); + auto reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); reader->setCard(MockCardConfig()); reader = MockReaderManagerPlugin::getInstance().addReader("ReaderMock"_L1); reader->setCard(MockCardConfig()); @@ -171,7 +175,7 @@ QFETCH(QByteArray, efCardAccess); QFETCH(QByteArray, eidType); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); reader->setCard(MockCardConfig(), EFCardAccess::decode(QByteArray::fromHex(efCardAccess)), CardType::SMART_EID); MessageDispatcher dispatcher; diff -Nru ausweisapp2-2.3.1/test/qt/ui/json/test_MsgHandlerReaderList.cpp ausweisapp2-2.4.0/test/qt/ui/json/test_MsgHandlerReaderList.cpp --- ausweisapp2-2.3.1/test/qt/ui/json/test_MsgHandlerReaderList.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/json/test_MsgHandlerReaderList.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,15 +5,17 @@ #include "MessageDispatcher.h" #include "MockReaderManagerPlugin.h" #include "ReaderManager.h" -#include "messages/MsgHandlerReader.h" #include + Q_IMPORT_PLUGIN(MockReaderManagerPlugin) + using namespace Qt::Literals::StringLiterals; using namespace governikus; + class test_MsgHandlerReaderList : public QObject { @@ -73,7 +75,7 @@ void oneReaderWithCard() { - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); reader->setCard(MockCardConfig()); MessageDispatcher dispatcher; @@ -95,7 +97,7 @@ { QFETCH(MsgLevel, msgLevel); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); + auto reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); reader->setCard(MockCardConfig()); reader = MockReaderManagerPlugin::getInstance().addReader("ReaderMock"_L1); reader->setCard(MockCardConfig()); diff -Nru ausweisapp2-2.3.1/test/qt/ui/json/test_MsgHandlerStatus.cpp ausweisapp2-2.4.0/test/qt/ui/json/test_MsgHandlerStatus.cpp --- ausweisapp2-2.3.1/test/qt/ui/json/test_MsgHandlerStatus.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/json/test_MsgHandlerStatus.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -221,9 +221,9 @@ QTest::addColumn("name"); QTest::newRow("AUTH") << Action::AUTH << QStringLiteral("AUTH"); - QTest::newRow("PIN") << Action::PIN << QStringLiteral("CHANGE_PIN"); + QTest::newRow("PIN") << Action::CHANGE_PIN << QStringLiteral("CHANGE_PIN"); QTest::newRow("PERSONALIZATION") << Action::PERSONALIZATION << QStringLiteral("PERSONALIZATION"); - QTest::newRow("SELF") << Action::SELF << QStringLiteral("UNKNOWN"); + QTest::newRow("SELF") << Action::SELF_AUTH << QStringLiteral("UNKNOWN"); QTest::newRow("REMOTE_SERVICE") << Action::REMOTE_SERVICE << QStringLiteral("UNKNOWN"); QCOMPARE(Enum::getCount(), 5); diff -Nru ausweisapp2-2.3.1/test/qt/ui/json/test_UiLoader.cpp ausweisapp2-2.4.0/test/qt/ui/json/test_UiLoader.cpp --- ausweisapp2-2.3.1/test/qt/ui/json/test_UiLoader.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/json/test_UiLoader.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -152,7 +152,7 @@ QTRY_COMPARE(spyDestroyed.count(), 1); // clazy:exclude=qstring-allocations // We call deleteLater and add a lambda with QueuedConnection. - // So the lambda will be disconnected if loader is destroyed abd + // So the lambda will be disconnected if loader is destroyed and // we never get a fireRemovedAllPlugins here. Even we don't have // blockSignals in dtor. QCOMPARE(spyShutdown.count(), 0); diff -Nru ausweisapp2-2.3.1/test/qt/ui/json/test_UiPluginJson.cpp ausweisapp2-2.4.0/test/qt/ui/json/test_UiPluginJson.cpp --- ausweisapp2-2.3.1/test/qt/ui/json/test_UiPluginJson.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/json/test_UiPluginJson.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -14,8 +14,10 @@ #include #include + Q_IMPORT_PLUGIN(MockReaderManagerPlugin) + using namespace Qt::Literals::StringLiterals; using namespace governikus; @@ -153,7 +155,7 @@ QSignalSpy spy(&api, &UiPluginJson::fireMessage); - MockReader* reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().addReader("MockReader 0815"_L1); reader->setCard(MockCardConfig()); QCOMPARE(spy.size(), 2); diff -Nru ausweisapp2-2.3.1/test/qt/ui/proxy/test_RedirectBroadcast.cpp ausweisapp2-2.4.0/test/qt/ui/proxy/test_RedirectBroadcast.cpp --- ausweisapp2-2.3.1/test/qt/ui/proxy/test_RedirectBroadcast.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/proxy/test_RedirectBroadcast.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#include "RedirectBroadcast.h" + +#include "PortFile.h" + +#include +#include + + +using namespace Qt::Literals::StringLiterals; +using namespace governikus; + + +class test_RedirectBroadcast + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void broadcast() + { +#ifdef Q_OS_WIN + QSKIP("Windows does not use portfiles"); +#endif + + QUdpSocket socket; + QVERIFY(socket.bind(QHostAddress::LocalHost)); + + QList messages; + connect(&socket, &QUdpSocket::readyRead, this, [&socket, &messages]{ + while (socket.hasPendingDatagrams()) + { + messages << socket.receiveDatagram(); + } + }); + + QTest::ignoreMessage(QtMsgType::QtDebugMsg, "Found instances on Ports: QList()"); + Q_UNUSED(new RedirectBroadcast(QNetworkDatagram("test"_ba), 1337)); + + PortFile file; + file.handlePort(socket.localPort()); + QTest::ignoreMessage(QtMsgType::QtDebugMsg, QRegularExpression(R"(Found instances on Ports: QList(\d*))"_L1)); + Q_UNUSED(new RedirectBroadcast(QNetworkDatagram("test"_ba), 1337)); + QTRY_COMPARE(messages.size(), 1); + QCOMPARE(messages.at(0).data(), "test"_ba); + } + + +}; + +QTEST_GUILESS_MAIN(test_RedirectBroadcast) +#include "test_RedirectBroadcast.moc" diff -Nru ausweisapp2-2.3.1/test/qt/ui/proxy/test_UiPluginProxy.cpp ausweisapp2-2.4.0/test/qt/ui/proxy/test_UiPluginProxy.cpp --- ausweisapp2-2.3.1/test/qt/ui/proxy/test_UiPluginProxy.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/proxy/test_UiPluginProxy.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -7,10 +7,15 @@ #include "Env.h" #include "HttpServer.h" +#include +#include #include + +using namespace Qt::Literals::StringLiterals; using namespace governikus; + class test_UiPluginProxy : public QObject { @@ -49,6 +54,21 @@ } + void broadcast() + { + UiPluginProxy proxy; + QVERIFY(proxy.initialize()); + + QSignalSpy spy(proxy.mSocket.data(), &QUdpSocket::readyRead); + QUdpSocket socket; + QVERIFY(socket.bind(QHostAddress::LocalHost)); + const auto port = Env::getShared()->getServerPort(); + QVERIFY(socket.writeDatagram("test"_ba, QHostAddress::LocalHost, port)); + QTest::ignoreMessage(QtDebugMsg, "Found instances on Ports: QList()"); + QTRY_VERIFY(!spy.isEmpty()); + } + + }; QTEST_GUILESS_MAIN(test_UiPluginProxy) diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/smart/test_SmartModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/smart/test_SmartModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/smart/test_SmartModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/smart/test_SmartModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -31,15 +31,15 @@ Q_OBJECT private: - QPointer mSmartReader; + QPointer mReader; void setupSmartReader(bool pPinInitial, int pRetryCounter) { - Q_ASSERT(mSmartReader); + Q_ASSERT(mReader); - auto info = mSmartReader->getReaderInfo(); + auto info = mReader->getReaderInfo(); info.setCardInfo(CardInfo(CardType::SMART_EID, FileRef(), QSharedPointer(), pRetryCounter, false, false, pPinInitial)); - mSmartReader->setReaderInfo(info); + mReader->setReaderInfo(info); } private Q_SLOTS: @@ -52,7 +52,7 @@ QTRY_COMPARE(spy.count(), 1); // clazy:exclude=qstring-allocations setSmartEidStatus(EidStatus::PERSONALIZED); - mSmartReader = MockReaderManagerPlugin::getInstance().addReader("SmartReader"_L1); + mReader = MockReaderManagerPlugin::getInstance().addReader("SmartReader"_L1); setupSmartReader(false, 3); const auto& smartModel = Env::getSingleton(); diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_AppUpdateDataModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_AppUpdateDataModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_AppUpdateDataModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_AppUpdateDataModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -61,6 +61,35 @@ } + void test_onAppcastFinished() + { + auto* model = Env::getSingleton(); + QVERIFY(model->getAppcastStatus().isEmpty()); + QStringList statusStrings; + + model->onAppcastFinished(false, GlobalStatus::Code::Downloader_Aborted); + QVERIFY(model->getAppcastStatus().isEmpty()); + + model->onAppcastFinished(true, GlobalStatus::Code::Downloader_Missing_Platform); + const auto& statusMissingPlatform = model->getAppcastStatus(); + QVERIFY(!statusMissingPlatform.isEmpty()); + QVERIFY(!statusStrings.contains(statusMissingPlatform)); + statusStrings.append(statusMissingPlatform); + + model->onAppcastFinished(false, GlobalStatus::Code::No_Error); + const auto& statusNoUpdateAvailable = model->getAppcastStatus(); + QVERIFY(!statusNoUpdateAvailable.isEmpty()); + QVERIFY(!statusStrings.contains(statusNoUpdateAvailable)); + statusStrings.append(statusNoUpdateAvailable); + + model->onAppcastFinished(true, GlobalStatus::Code::No_Error); + const auto& statusUpdateAvailable = model->getAppcastStatus(); + QVERIFY(!statusUpdateAvailable.isEmpty()); + QVERIFY(!statusStrings.contains(statusUpdateAvailable)); + statusStrings.append(statusUpdateAvailable); + } + + }; QTEST_GUILESS_MAIN(test_AppUpdateDataModel) diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_ApplicationModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_ApplicationModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_ApplicationModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_ApplicationModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -5,6 +5,8 @@ #include "ApplicationModel.h" #include "MockIfdServer.h" +#include "MockReaderManagerPlugin.h" +#include "ReaderManager.h" #include "context/AuthContext.h" #include "context/ChangePinContext.h" #include "context/IfdServiceContext.h" @@ -17,12 +19,37 @@ using namespace governikus; +Q_IMPORT_PLUGIN(MockReaderManagerPlugin) + class test_ApplicationModel : public QObject { Q_OBJECT private Q_SLOTS: + void initTestCase() + { + QThread::currentThread()->setObjectName(QStringLiteral("MainThread")); + + auto* readerManager = Env::getSingleton(); + QSignalSpy spy(readerManager, &ReaderManager::fireInitialized); + readerManager->init(); + QTRY_COMPARE(spy.count(), 1); // clazy:exclude=qstring-allocations + } + + + void cleanupTestCase() + { + Env::getSingleton()->shutdown(); + } + + + void cleanup() + { + MockReaderManagerPlugin::getInstance().removeAllReader(); + } + + void test_getFeedbackTimeout() { QCOMPARE(ApplicationModel::getFeedbackTimeout(), 7000); @@ -80,6 +107,45 @@ } + void test_usedPluginType_data() + { + QTest::addColumn("usedReader"); + QTest::addColumn("readerName"); + QTest::addColumn("readerType"); + QTest::addColumn("usedPluginType"); + + QTest::addRow("No used reader - PCSC") << "" << "PcscReader" << ReaderManagerPluginType::PCSC << ReaderManagerPluginType::UNKNOWN; + QTest::addRow("No used reader - Remote") << "" << "RemoteReader" << ReaderManagerPluginType::REMOTE_IFD << ReaderManagerPluginType::UNKNOWN; + + QTest::addRow("PCSC") << "PcscReader" << "PcscReader" << ReaderManagerPluginType::PCSC << ReaderManagerPluginType::PCSC; + QTest::addRow("Remote") << "RemoteReader" << "RemoteReader" << ReaderManagerPluginType::REMOTE_IFD << ReaderManagerPluginType::REMOTE_IFD; + + QTest::addRow("Other Reader 1") << "RemoteReader" << "PcscReader" << ReaderManagerPluginType::PCSC << ReaderManagerPluginType::UNKNOWN; + QTest::addRow("Other Reader 2") << "PcscReader" << "RemoteReader" << ReaderManagerPluginType::REMOTE_IFD << ReaderManagerPluginType::UNKNOWN; + } + + + void test_usedPluginType() + { + QFETCH(QString, usedReader); + QFETCH(QString, readerName); + QFETCH(ReaderManagerPluginType, readerType); + QFETCH(ReaderManagerPluginType, usedPluginType); + + QSharedPointer context(new AuthContext()); + context->setReaderName(usedReader); + if (!readerName.isEmpty()) + { + MockReaderManagerPlugin::getInstance().addReader(readerName, readerType); + } + + const auto model = Env::getSingleton(); + model->resetContext(context); + + QCOMPARE(model->getUsedPluginType(), usedPluginType); + } + + }; QTEST_MAIN(test_ApplicationModel) diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_AuthModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_AuthModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_AuthModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_AuthModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -141,6 +141,9 @@ context->setTcTokenUrl(QUrl("https://www.governikus.de/tcToken"_L1)); QCOMPARE(model->getErrorHeader(), QStringLiteral("https://www.governikus.de")); + + context->setStatus(GlobalStatus::Code::Workflow_Card_Removed); + QCOMPARE(model->getErrorHeader(), tr("Connection to ID card lost")); } @@ -184,35 +187,6 @@ } - void test_getStatusCodeString_data() - { - QTest::addColumn>("context"); - QTest::addColumn("statusCode"); - QTest::addColumn("result"); - - QTest::addRow("No context") << QSharedPointer(nullptr) << GlobalStatus::Code::No_Error << "Unknown_Error"; - QTest::addRow("Any error") << QSharedPointer::create() << GlobalStatus::Code::Card_Communication_Error << "Card_Communication_Error"; - QTest::addRow("No error") << QSharedPointer::create() << GlobalStatus::Code::No_Error << "No_Error"; - } - - - void test_getStatusCodeString() - { - QFETCH(QSharedPointer, context); - QFETCH(GlobalStatus::Code, statusCode); - QFETCH(QString, result); - - auto* const model = Env::getSingleton(); - if (context) - { - context->setStatus(GlobalStatus(statusCode)); - } - model->resetAuthContext(context); - - QCOMPARE(model->getStatusCodeString(), result); - } - - void test_resultViewButtonIcon_data() { QTest::addColumn>("context"); @@ -304,6 +278,29 @@ } + void test_getRefreshUrl_data() + { + QTest::addColumn("refreshUrl"); + + QTest::addRow("Empty URL") << QUrl(""_L1); + QTest::addRow("Non-empty URL") << QUrl("https://www.foo.bar?ResultMajor=ok"_L1); + } + + + void test_getRefreshUrl() + { + QFETCH(QUrl, refreshUrl); + + auto context = QSharedPointer::create(); + context->setRefreshUrl(refreshUrl); + + auto* const model = Env::getSingleton(); + model->resetAuthContext(context); + + QCOMPARE(model->getRefreshUrl(), refreshUrl); + } + + }; QTEST_MAIN(test_AuthModel) diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_CardPosition.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_CardPosition.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_CardPosition.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_CardPosition.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#include "CardPosition.h" + +#include + + +using namespace governikus; + + +class test_CardPosition + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void defaultValues() + { + CardPosition cardPosition; + QCOMPARE(cardPosition.getXPosition(), 0.0); + QCOMPARE(cardPosition.getYPosition(), 0.0); + QCOMPARE(cardPosition.getRotation(), 0.0); + QCOMPARE(cardPosition.getZPosition(), -1); + } + + + void values() + { + CardPosition cardPosition({0.1, 0.2, 0.3, 1}); + QCOMPARE(cardPosition.getXPosition(), 0.1); + QCOMPARE(cardPosition.getYPosition(), 0.2); + QCOMPARE(cardPosition.getRotation(), 0.3); + QCOMPARE(cardPosition.getZPosition(), 1); + } + + + void comparator() + { + QVERIFY(CardPosition({0.1, 0.2, 0.3, 1}) == CardPosition({0.1, 0.2, 0.3, 1})); + QVERIFY(CardPosition({0.0, 0.2, 0.3, 1}) != CardPosition({0.1, 0.2, 0.3, 1})); + QVERIFY(CardPosition({0.1, 0.0, 0.3, 1}) != CardPosition({0.1, 0.2, 0.3, 1})); + QVERIFY(CardPosition({0.1, 0.2, 0.0, 1}) != CardPosition({0.1, 0.2, 0.3, 1})); + QVERIFY(CardPosition({0.1, 0.2, 0.3, -1}) != CardPosition({0.1, 0.2, 0.3, 1})); + } + + +}; + +QTEST_MAIN(test_CardPosition) +#include "test_CardPosition.moc" diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_CardPositionModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_CardPositionModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_CardPositionModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_CardPositionModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -7,15 +7,8 @@ #include #include -using namespace governikus; -bool operator==(const CardPosition& pLeft, const CardPosition& pRight) -{ - return pLeft.mXPosition == pRight.mXPosition - && pLeft.mYPosition == pRight.mYPosition - && pLeft.mZPosition == pRight.mZPosition - && pLeft.mRotation == pRight.mRotation; -} +using namespace governikus; class test_CardPositionModel @@ -75,9 +68,9 @@ QCOMPARE(mModel->getIsRunning(), false); QScopedPointer cardPosition(qvariant_cast(mModel->getCardPosition())); #if defined(Q_OS_IOS) - QVERIFY(CardPosition(0.5, 0.0, -1) == *cardPosition); + QVERIFY(CardPosition({0.5, 0.0}) == *cardPosition); #else - QVERIFY(CardPosition(0.5, 0.5, -1, 90) == *cardPosition); + QVERIFY(CardPosition({0.5, 0.5, 90}) == *cardPosition); #endif } @@ -92,38 +85,38 @@ QCOMPARE(mModel->getIsRunning(), true); QCOMPARE(spyCardPositionChanged.count(), 1); QScopedPointer cardPosition(qvariant_cast(mModel->getCardPosition())); - QVERIFY(CardPosition(0.5, 0.5, -1, 90) == *cardPosition); + QVERIFY(CardPosition({0.5, 0.5, 90}) == *cardPosition); mModel->onTimerTimeout(); QCOMPARE(spyCardPositionChanged.count(), 2); cardPosition.reset(qvariant_cast(mModel->getCardPosition())); - QVERIFY(CardPosition(0.5, 0.5, -1) == *cardPosition); + QVERIFY(CardPosition({0.5, 0.5}) == *cardPosition); mModel->onTimerTimeout(); QCOMPARE(spyCardPositionChanged.count(), 3); cardPosition.reset(qvariant_cast(mModel->getCardPosition())); - QVERIFY(CardPosition(0.5, 0.25, -1) == *cardPosition); + QVERIFY(CardPosition({0.5, 0.25}) == *cardPosition); mModel->onTimerTimeout(); QCOMPARE(spyCardPositionChanged.count(), 4); cardPosition.reset(qvariant_cast(mModel->getCardPosition())); - QVERIFY(CardPosition(0.5, 0.75, -1) == *cardPosition); + QVERIFY(CardPosition({0.5, 0.75}) == *cardPosition); mModel->onTimerTimeout(); QCOMPARE(spyCardPositionChanged.count(), 5); cardPosition.reset(qvariant_cast(mModel->getCardPosition())); - QVERIFY(CardPosition(0.5, 0.5, -1, 90) == *cardPosition); + QVERIFY(CardPosition({0.5, 0.5, 90}) == *cardPosition); mModel->onTimerTimeout(); QCOMPARE(spyCardPositionChanged.count(), 6); cardPosition.reset(qvariant_cast(mModel->getCardPosition())); - QVERIFY(CardPosition(0.5, 0.5, -1) == *cardPosition); + QVERIFY(CardPosition({0.5, 0.5}) == *cardPosition); mModel->setIsRunning(false); mModel->setIsRunning(true); QCOMPARE(spyCardPositionChanged.count(), 7); cardPosition.reset(qvariant_cast(mModel->getCardPosition())); - QVERIFY(CardPosition(0.5, 0.5, -1, 90) == *cardPosition); + QVERIFY(CardPosition({0.5, 0.5, 90}) == *cardPosition); } diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_CertificateDescriptionModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_CertificateDescriptionModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_CertificateDescriptionModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_CertificateDescriptionModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -56,14 +56,14 @@ { QSignalSpy spy(mModel, &CertificateDescriptionModel::fireChanged); mModel->resetContext(mContext); - QCOMPARE(mModel->rowCount(), 4); + QCOMPARE(mModel->rowCount(QModelIndex()), 4); const QString termsOfUsage = QStringLiteral("Name, Anschrift und E-Mail-Adresse des Diensteanbieters:\nGovernikus GmbH & Co. KG\nHochschulring 4\n28359 Bremen\nE-Mail: kontakt@governikus.de "); QCOMPARE(mModel->data(mModel->index(0), CertificateDescriptionModel::UserRoles::LABEL), "Provider"_L1); QCOMPARE(mModel->data(mModel->index(0), CertificateDescriptionModel::UserRoles::TEXT), QStringLiteral("Governikus GmbH & Co. KG\nhttps://test.governikus-eid.de")); QCOMPARE(mModel->data(mModel->index(1), CertificateDescriptionModel::UserRoles::LABEL), "Certificate issuer"_L1); QCOMPARE(mModel->data(mModel->index(1), CertificateDescriptionModel::UserRoles::TEXT), QStringLiteral("Governikus Test DVCA\nhttp://www.governikus.de")); - QCOMPARE(mModel->data(mModel->index(2), CertificateDescriptionModel::UserRoles::LABEL), "Provider information"_L1); + QCOMPARE(mModel->data(mModel->index(2), CertificateDescriptionModel::UserRoles::LABEL), "Provider Information"_L1); QCOMPARE(mModel->data(mModel->index(2), CertificateDescriptionModel::UserRoles::TEXT), termsOfUsage); QCOMPARE(mModel->data(mModel->index(3), CertificateDescriptionModel::UserRoles::LABEL), "Validity"_L1); QCOMPARE(spy.count(), 1); @@ -118,7 +118,7 @@ QCOMPARE(mModel->data(mModel->index(0), CertificateDescriptionModel::UserRoles::TEXT), "Gesamtverband der deutschen Versicherungswirtschaft e.V.\n"_L1); QCOMPARE(mModel->data(mModel->index(1), CertificateDescriptionModel::UserRoles::LABEL), "Certificate issuer"_L1); QCOMPARE(mModel->data(mModel->index(1), CertificateDescriptionModel::UserRoles::TEXT), "D-Trust GmbH\n"_L1); - QCOMPARE(mModel->data(mModel->index(2), CertificateDescriptionModel::UserRoles::LABEL), "Provider information"_L1); + QCOMPARE(mModel->data(mModel->index(2), CertificateDescriptionModel::UserRoles::LABEL), "Provider Information"_L1); } diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_Email.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_Email.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_Email.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_Email.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -29,7 +29,7 @@ void test_generateMailBody_data() { - QTest::addColumn("serviceUrl"); + QTest::addColumn("serviceUrl"); QTest::addColumn("percentEncoding"); QTest::addColumn("result"); @@ -55,18 +55,18 @@ Parameter of occurred error: https://www.test.de)"_s; - QTest::newRow("service - percent") << QStringLiteral("https://www.foo.bar") << true << resultWithService; - QTest::newRow("service - no percent") << QStringLiteral("https://www.foo.bar") << false << QString(resultWithService).replace("%20"_L1, " "_L1); + QTest::newRow("service - percent") << QUrl(QStringLiteral("https://www.foo.bar")) << true << resultWithService; + QTest::newRow("service - no percent") << QUrl(QStringLiteral("https://www.foo.bar")) << false << QString(resultWithService).replace("%20"_L1, " "_L1); const auto resultWithoutService = QString(resultWithService).remove(346, 34); - QTest::newRow("no service - percent") << QString() << true << resultWithoutService; - QTest::newRow("no service - no percent") << QString() << false << QString(resultWithoutService).replace("%20"_L1, " "_L1); + QTest::newRow("no service - percent") << QUrl() << true << resultWithoutService; + QTest::newRow("no service - no percent") << QUrl() << false << QString(resultWithoutService).replace("%20"_L1, " "_L1); } void test_generateMailBody() { - QFETCH(QString, serviceUrl); + QFETCH(QUrl, serviceUrl); QFETCH(bool, percentEncoding); QFETCH(QString, result); diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_FormattedTextModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_FormattedTextModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_FormattedTextModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_FormattedTextModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,9 +6,12 @@ #include +#include + + using namespace governikus; -using PairList = QList>; +using PairList = QList>; class test_FormattedTextModel : public QObject @@ -123,26 +126,26 @@ QTest::addColumn("modelContent"); PairList fileContent; - fileContent << qMakePair(QStringLiteral("This is a header"), FormattedTextModel::LineType::HEADER); - fileContent << qMakePair(QString(), FormattedTextModel::LineType::EMPTY); - fileContent << qMakePair(QStringLiteral("First section"), FormattedTextModel::LineType::SECTION); - fileContent << qMakePair(QString(), FormattedTextModel::LineType::EMPTY); - fileContent << qMakePair(QStringLiteral("Subsection one"), FormattedTextModel::LineType::SUBSECTION); - fileContent << qMakePair(QString(), FormattedTextModel::LineType::EMPTY); - fileContent << qMakePair(QStringLiteral("This multiline text will be concatenated to one text."), FormattedTextModel::LineType::REGULARTEXT); - fileContent << qMakePair(QString(), FormattedTextModel::LineType::EMPTY); - fileContent << qMakePair(QStringLiteral("Subsection two"), FormattedTextModel::LineType::SUBSECTION); - fileContent << qMakePair(QString(), FormattedTextModel::LineType::EMPTY); - fileContent << qMakePair(QStringLiteral("Some list items:"), FormattedTextModel::LineType::REGULARTEXT); - fileContent << qMakePair(QString(), FormattedTextModel::LineType::EMPTY); - fileContent << qMakePair(QStringLiteral("First"), FormattedTextModel::LineType::LISTITEM); - fileContent << qMakePair(QString(), FormattedTextModel::LineType::EMPTY); - fileContent << qMakePair(QStringLiteral("Second"), FormattedTextModel::LineType::LISTITEM); - fileContent << qMakePair(QString(), FormattedTextModel::LineType::EMPTY); - fileContent << qMakePair(QStringLiteral("Third"), FormattedTextModel::LineType::LISTITEM); + fileContent << std::make_pair(QStringLiteral("This is a header"), FormattedTextModel::LineType::HEADER); + fileContent << std::make_pair(QString(), FormattedTextModel::LineType::EMPTY); + fileContent << std::make_pair(QStringLiteral("First section"), FormattedTextModel::LineType::SECTION); + fileContent << std::make_pair(QString(), FormattedTextModel::LineType::EMPTY); + fileContent << std::make_pair(QStringLiteral("Subsection one"), FormattedTextModel::LineType::SUBSECTION); + fileContent << std::make_pair(QString(), FormattedTextModel::LineType::EMPTY); + fileContent << std::make_pair(QStringLiteral("This multiline text will be concatenated to one text."), FormattedTextModel::LineType::REGULARTEXT); + fileContent << std::make_pair(QString(), FormattedTextModel::LineType::EMPTY); + fileContent << std::make_pair(QStringLiteral("Subsection two"), FormattedTextModel::LineType::SUBSECTION); + fileContent << std::make_pair(QString(), FormattedTextModel::LineType::EMPTY); + fileContent << std::make_pair(QStringLiteral("Some list items:"), FormattedTextModel::LineType::REGULARTEXT); + fileContent << std::make_pair(QString(), FormattedTextModel::LineType::EMPTY); + fileContent << std::make_pair(QStringLiteral("First"), FormattedTextModel::LineType::LISTITEM); + fileContent << std::make_pair(QString(), FormattedTextModel::LineType::EMPTY); + fileContent << std::make_pair(QStringLiteral("Second"), FormattedTextModel::LineType::LISTITEM); + fileContent << std::make_pair(QString(), FormattedTextModel::LineType::EMPTY); + fileContent << std::make_pair(QStringLiteral("Third"), FormattedTextModel::LineType::LISTITEM); PairList fileSeparator; - fileSeparator << qMakePair(QString(), FormattedTextModel::LineType::EMPTY); + fileSeparator << std::make_pair(QString(), FormattedTextModel::LineType::EMPTY); QTest::addRow("formattedText.txt") << ":/qml/formattedText.txt" << 35 << fileContent + fileSeparator + fileContent; } @@ -156,9 +159,9 @@ FormattedTextModel textModel(nullptr); QCOMPARE(textModel.loadSeveral({textFile, textFile}), true); - QCOMPARE(textModel.rowCount(), rowCount); + QCOMPARE(textModel.rowCount(QModelIndex()), rowCount); - for (int i = 0; i < textModel.rowCount(); ++i) + for (int i = 0; i < rowCount; ++i) { const auto modelIndex = textModel.index(i); QCOMPARE(textModel.data(modelIndex, FormattedTextModel::ContentRole), modelContent.at(i).first); diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_LogFilesModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_LogFilesModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_LogFilesModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_LogFilesModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2018-2025 Governikus GmbH & Co. KG, Germany + */ + +#include "LogFilesModel.h" + +#include "LogHandler.h" + +#include +#include +#include + +using namespace Qt::Literals::StringLiterals; +using namespace governikus; + +// convenient helper class to create and open a self-deleting temporary file +struct TemporaryLogFile // clazy:exclude=rule-of-three +{ + QString mUniqueName; + + TemporaryLogFile(const QString& pTemplateName) + { + // Use QTemporaryFile to obtain a unique name, as in the LogHandler class. + QTemporaryFile tempFile(pTemplateName); + // On Windows, calling removeOtherLogFiles() immediately after opening the file fails, + // therefore, the file is deleted manually in the destructor. + tempFile.setAutoRemove(false); + QVERIFY(tempFile.open()); + // On Linux, QTemporaryFile attempts to create unnamed temporary files, + // fileName() call is needed to find that files. + mUniqueName = tempFile.fileName(); + } + + + ~TemporaryLogFile() + { + QFile file(mUniqueName); + if (file.exists()) + { + QVERIFY(file.remove()); + } + } + + +}; + + +class test_LogFilesModel + : public QObject +{ + Q_OBJECT + + private Q_SLOTS: + void initTestCase() + { + LogHandler::getInstance().init(); + } + + + void cleanup() + { + LogHandler::getInstance().removeOtherLogFiles(); + } + + + void test_GetSelectedLogFileName() + { + TemporaryLogFile oldLogFile(LogHandler::getLogFileTemplate()); + LogFilesModel model; + + QCOMPARE(model.getLogFileName(0), QLatin1String("Current log")); + QVERIFY(!model.getLogFileName(1).isEmpty()); + } + + + void test_RemoveOtherLogFiles() + { + TemporaryLogFile oldLogFile(LogHandler::getLogFileTemplate()); + LogFilesModel model; + QVERIFY(model.getCount() == 2); + + model.removeOtherLogFiles(); + + QCOMPARE(model.getCount(), 1); + } + + + void test_GetSelectedLogFile_first_isAlwaysEmptyString() + { + LogFilesModel model; + QCOMPARE(model.getLogFilePath(0), QString()); + } + + + void test_GetSelectedLogFile_notFirst_isExistingFile() + { + TemporaryLogFile oldLogFile(LogHandler::getLogFileTemplate()); + LogFilesModel model; + QVERIFY(model.getCount() == 2); + QCOMPARE(model.getLogFilePath(1), oldLogFile.mUniqueName); + } + + + void test_saveDummyLogFile() + { + LogFilesModel model; + const auto fileCount = model.getCount(); + + model.saveDummyLogFile(); + + QCOMPARE(model.getCount(), fileCount + 1); + } + + +}; + +QTEST_GUILESS_MAIN(test_LogFilesModel) +#include "test_LogFilesModel.moc" diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_LogFilterModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_LogFilterModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_LogFilterModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_LogFilterModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -31,62 +31,70 @@ void filtering() { + LogModel logModel; + logModel.setSource(QString()); LogFilterModel model; + model.setSourceModel(&logModel); qCDebug(card) << "test"; qCWarning(card) << "test"; QCOMPARE(model.getLevels(), QStringList({"D"_L1, "W"_L1})); QCOMPARE(model.getSelectedLevels(), QStringList()); - QCOMPARE(model.getCategories(), QStringList({"card"_L1, "default"_L1})); + QCOMPARE(model.getCategories(), QStringList({"card"_L1})); QCOMPARE(model.getSelectedCategories(), QStringList()); - QCOMPARE(model.rowCount(), 3); + QCOMPARE(model.rowCount(), 2); QSignalSpy spyLevel(&model, &LogFilterModel::fireLevelsChanged); QSignalSpy spyCategories(&model, &LogFilterModel::fireCategoriesChanged); qCDebug(gui) << "test"; + QCOMPARE(spyLevel.count(), 0); QCOMPARE(model.getLevels(), QStringList({"D"_L1, "W"_L1})); QCOMPARE(model.getSelectedLevels(), QStringList()); QCOMPARE(spyCategories.count(), 1); - QCOMPARE(model.getCategories(), QStringList({"card"_L1, "default"_L1, "gui"_L1})); + QCOMPARE(model.getCategories(), QStringList({"card"_L1, "gui"_L1})); QCOMPARE(model.getSelectedCategories(), QStringList()); - QCOMPARE(model.rowCount(), 4); + QCOMPARE(model.rowCount(), 3); qCInfo(gui) << "test"; + QCOMPARE(spyLevel.count(), 1); QCOMPARE(model.getLevels(), QStringList({"D"_L1, "I"_L1, "W"_L1})); QCOMPARE(model.getSelectedLevels(), QStringList()); QCOMPARE(spyCategories.count(), 1); - QCOMPARE(model.getCategories(), QStringList({"card"_L1, "default"_L1, "gui"_L1})); + QCOMPARE(model.getCategories(), QStringList({"card"_L1, "gui"_L1})); QCOMPARE(model.getSelectedCategories(), QStringList()); - QCOMPARE(model.rowCount(), 5); + QCOMPARE(model.rowCount(), 4); model.configureLevel("D"_L1, true); + QCOMPARE(spyLevel.count(), 2); QCOMPARE(model.getLevels(), QStringList({"D"_L1, "I"_L1, "W"_L1})); QCOMPARE(model.getSelectedLevels(), QStringList({"D"_L1})); QCOMPARE(spyCategories.count(), 1); - QCOMPARE(model.getCategories(), QStringList({"card"_L1, "default"_L1, "gui"_L1})); + QCOMPARE(model.getCategories(), QStringList({"card"_L1, "gui"_L1})); QCOMPARE(model.getSelectedCategories(), QStringList()); - QCOMPARE(model.rowCount(), 3); + QCOMPARE(model.rowCount(), 2); model.configureCategory("card"_L1, true); + QCOMPARE(spyLevel.count(), 2); QCOMPARE(model.getLevels(), QStringList({"D"_L1, "I"_L1, "W"_L1})); QCOMPARE(model.getSelectedLevels(), QStringList({"D"_L1})); QCOMPARE(spyCategories.count(), 2); - QCOMPARE(model.getCategories(), QStringList({"card"_L1, "default"_L1, "gui"_L1})); + QCOMPARE(model.getCategories(), QStringList({"card"_L1, "gui"_L1})); QCOMPARE(model.getSelectedCategories(), QStringList({"card"_L1})); QCOMPARE(model.rowCount(), 1); model.configureLevel("D"_L1, false); + QCOMPARE(spyLevel.count(), 3); QCOMPARE(model.getLevels(), QStringList({"D"_L1, "I"_L1, "W"_L1})); QCOMPARE(model.getSelectedLevels(), QStringList()); QCOMPARE(spyCategories.count(), 2); - QCOMPARE(model.getCategories(), QStringList({"card"_L1, "default"_L1, "gui"_L1})); + QCOMPARE(model.getCategories(), QStringList({"card"_L1, "gui"_L1})); QCOMPARE(model.getSelectedCategories(), QStringList({"card"_L1})); QCOMPARE(model.rowCount(), 2); } diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_LogModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_LogModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_LogModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_LogModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -7,7 +7,8 @@ #include "LogHandler.h" #include -#include +#include +#include using namespace Qt::Literals::StringLiterals; using namespace governikus; @@ -85,7 +86,7 @@ QFETCH(QLatin1String, message); QSignalSpy spyLevel(mModel, &LogModel::fireLevelsChanged); - QSignalSpy spyCategorie(mModel, &LogModel::fireCategoriesChanged); + QSignalSpy spyCategory(mModel, &LogModel::fireCategoriesChanged); mModel->addLogEntry(input); QCOMPARE(mModel->mLogEntries.at(0), input); @@ -100,7 +101,7 @@ QCOMPARE(mModel->getLevels().size(), 1); QVERIFY(mModel->getLevels().contains(level)); - QCOMPARE(spyCategorie.size(), 1); + QCOMPARE(spyCategory.size(), 1); QCOMPARE(mModel->getCategories().size(), 1); QVERIFY(mModel->getCategories().contains(category)); } @@ -109,68 +110,68 @@ void test_LevelCategory() { QSignalSpy spyLevel(mModel, &LogModel::fireLevelsChanged); - QSignalSpy spyCategorie(mModel, &LogModel::fireCategoriesChanged); + QSignalSpy spyCategory(mModel, &LogModel::fireCategoriesChanged); mModel->addLogEntry("cat 0000.00.00 00:00:00.000 000 W test : test"_L1); QCOMPARE(spyLevel.size(), 1); QCOMPARE(mModel->getLevels(), QSet({"W"_L1})); - QCOMPARE(spyCategorie.size(), 1); + QCOMPARE(spyCategory.size(), 1); QCOMPARE(mModel->getCategories(), QSet({"cat"_L1})); mModel->addLogEntry("cat 0000.00.00 00:00:00.000 000 W test : test"_L1); QCOMPARE(spyLevel.size(), 1); QCOMPARE(mModel->getLevels(), QSet({"W"_L1})); - QCOMPARE(spyCategorie.size(), 1); + QCOMPARE(spyCategory.size(), 1); QCOMPARE(mModel->getCategories(), QSet({"cat"_L1})); mModel->addLogEntry("cat 0000.00.00 00:00:00.000 000 I test : test"_L1); QCOMPARE(spyLevel.size(), 2); QCOMPARE(mModel->getLevels(), QSet({"W"_L1, "I"_L1})); - QCOMPARE(spyCategorie.size(), 1); + QCOMPARE(spyCategory.size(), 1); QCOMPARE(mModel->getCategories(), QSet({"cat"_L1})); mModel->addLogEntry("dog 0000.00.00 00:00:00.000 000 I test : test"_L1); QCOMPARE(spyLevel.size(), 2); QCOMPARE(mModel->getLevels(), QSet({"W"_L1, "I"_L1})); - QCOMPARE(spyCategorie.size(), 2); + QCOMPARE(spyCategory.size(), 2); QCOMPARE(mModel->getCategories(), QSet({"cat"_L1, "dog"_L1})); mModel->addLogEntry("dog 0000.00.00 00:00:00.000 000 I test : test"_L1); QCOMPARE(spyLevel.size(), 2); QCOMPARE(mModel->getLevels(), QSet({"W"_L1, "I"_L1})); - QCOMPARE(spyCategorie.size(), 2); + QCOMPARE(spyCategory.size(), 2); QCOMPARE(mModel->getCategories(), QSet({"cat"_L1, "dog"_L1})); } void test_AddMultilineLogEntry() { - mModel->addLogEntry(QLatin1String("FooBar")); + mModel->addLogEntry(QStringLiteral("FooBar")); QCOMPARE(mModel->mLogEntries.size(), 1); QCOMPARE(mModel->mLogEntries.at(0), QLatin1String("FooBar")); - mModel->addLogEntry(QLatin1String("FooBar")); + mModel->addLogEntry(QStringLiteral("FooBar")); QCOMPARE(mModel->mLogEntries.size(), 1); QCOMPARE(mModel->mLogEntries.at(0), QLatin1String("FooBar\nFooBar")); - mModel->addLogEntry(QLatin1String("cat 0000.00.00 00:00:00.000 000 test")); + mModel->addLogEntry(QStringLiteral("cat 0000.00.00 00:00:00.000 000 test")); QCOMPARE(mModel->mLogEntries.size(), 2); QCOMPARE(mModel->mLogEntries.at(0), QLatin1String("FooBar\nFooBar")); QCOMPARE(mModel->mLogEntries.at(1), QLatin1String("cat 0000.00.00 00:00:00.000 000 test")); - mModel->addLogEntry(QLatin1String("cat 0001.02.03 04:05:06.007 0000008 test")); + mModel->addLogEntry(QStringLiteral("cat 0001.02.03 04:05:06.007 0000008 test")); QCOMPARE(mModel->mLogEntries.size(), 3); QCOMPARE(mModel->mLogEntries.at(0), QLatin1String("FooBar\nFooBar")); QCOMPARE(mModel->mLogEntries.at(1), QLatin1String("cat 0000.00.00 00:00:00.000 000 test")); QCOMPARE(mModel->mLogEntries.at(2), QLatin1String("cat 0001.02.03 04:05:06.007 0000008 test")); - mModel->addLogEntry(QLatin1String("BarFoo")); + mModel->addLogEntry(QStringLiteral("BarFoo")); QCOMPARE(mModel->mLogEntries.size(), 3); QCOMPARE(mModel->mLogEntries.at(0), QLatin1String("FooBar\nFooBar")); QCOMPARE(mModel->mLogEntries.at(1), QLatin1String("cat 0000.00.00 00:00:00.000 000 test")); QCOMPARE(mModel->mLogEntries.at(2), QLatin1String("cat 0001.02.03 04:05:06.007 0000008 test\nBarFoo")); - mModel->addLogEntry(QLatin1String("cat 0000.00.00 00:00:00.000 000 test")); + mModel->addLogEntry(QStringLiteral("cat 0000.00.00 00:00:00.000 000 test")); QCOMPARE(mModel->mLogEntries.size(), 3); QCOMPARE(mModel->mLogEntries.at(0), QLatin1String("FooBar\nFooBar")); QCOMPARE(mModel->mLogEntries.at(1), QLatin1String("cat 0000.00.00 00:00:00.000 000 test")); @@ -178,16 +179,6 @@ } - void test_CurrentLog() - { - qDebug() << "log line 1"; - qDebug() << "log line 2"; - qDebug() << "log line 3"; - mModel->setLogFile(0); - QCOMPARE(mModel->mLogEntries.size(), 3); - } - - void test_SetLogEntries_data() { QTest::addColumn("fileName"); @@ -211,7 +202,7 @@ QSignalSpy spyNewLogMsg(mModel, &LogModel::fireNewLogMsg); QSignalSpy spyLevel(mModel, &LogModel::fireLevelsChanged); - QSignalSpy spyCategorie(mModel, &LogModel::fireCategoriesChanged); + QSignalSpy spyCategory(mModel, &LogModel::fireCategoriesChanged); QFile file(fileName); QVERIFY(file.open(QIODevice::ReadOnly)); @@ -222,65 +213,48 @@ QCOMPARE(mModel->mLogEntries.size(), logEntriesSize); QCOMPARE(spyLevel.size(), 1); QCOMPARE(mModel->getLevels().size(), logLevelSize); - QCOMPARE(spyCategorie.size(), 1); + QCOMPARE(spyCategory.size(), 1); QCOMPARE(mModel->getCategories().size(), logCategoriesSize); } - void test_OnNewLogMsg_data() + void test_OnNewLogMsg_currentLog_messageAddedAndSignalFired() { - QTest::addColumn("msg"); - QTest::addColumn("fileName"); - QTest::addColumn("selectedFile"); - QTest::addColumn("newLogMsgCounter"); - QTest::addColumn("logEntriesSizeChange"); + mModel->setSource(QString()); + QSignalSpy signalSpy(mModel, &LogModel::fireNewLogMsg); + const auto oldMsgCount = mModel->rowCount(QModelIndex()); - QTest::newRow("emptyFile_MsgAdded") << QLatin1String("test : input") << QLatin1String(":/logfiles/empty.txt") << 0 << 1 << 1; - QTest::newRow("emptyFile_MsgNotAdded") << QLatin1String(" : ") << QLatin1String(":/logfiles/empty.txt") << 1 << 0 << 0; + qDebug() << "test : input"; - QTest::newRow("size1_MsgAdded") << QLatin1String("test : input") << QLatin1String(":/logfiles/size1.txt") << 0 << 1 << 1; - QTest::newRow("size1_MsgNotAdded") << QLatin1String(" : ") << QLatin1String(":/logfiles/size1.txt") << 1 << 0 << 0; + QCOMPARE(mModel->isCurrentLog(), true); + QCOMPARE(signalSpy.count(), 1); + QCOMPARE(mModel->rowCount(QModelIndex()), oldMsgCount + 1); } - void test_OnNewLogMsg() + void test_OnNewLogMsg_oldLog_messageIgnored() { - QFETCH(QLatin1String, msg); - QFETCH(QLatin1String, fileName); - QFETCH(int, selectedFile); - QFETCH(int, newLogMsgCounter); - QFETCH(int, logEntriesSizeChange); + mModel->setSource(QStringLiteral(":/logfiles/empty.txt")); + QSignalSpy signalSpy(mModel, &LogModel::fireNewLogMsg); + const auto oldMsgCount = mModel->rowCount(QModelIndex()); - QFile file(fileName); - QVERIFY(file.open(QIODevice::ReadOnly)); - QTextStream stream(&file); - mModel->setLogEntries(stream); - const auto oldSize = mModel->mLogEntries.size(); - mModel->mSelectedLogFile = selectedFile; - QSignalSpy spyNewLogMsg(mModel, &LogModel::fireNewLogMsg); + qDebug() << "test : input"; - qDebug() << msg; - QCOMPARE(spyNewLogMsg.count(), newLogMsgCounter); - QCOMPARE(mModel->mLogEntries.size(), oldSize + logEntriesSizeChange); + QCOMPARE(mModel->isCurrentLog(), false); + QCOMPARE(signalSpy.count(), 0); + QCOMPARE(mModel->rowCount(QModelIndex()), oldMsgCount); } - void test_RemoveOldLogFile() + void test_createLogFileName() { - { - QTemporaryFile oldLogFile(LogHandler::getLogFileTemplate()); - oldLogFile.setAutoRemove(false); - QVERIFY(oldLogFile.open()); - QVERIFY(!oldLogFile.fileName().isNull()); - } - - // We need to reset() the model since the list of "old" logfiles - // is only populated in the ctor of LogModel. - resetModel(new LogModel()); + const auto& appName = QCoreApplication::applicationName(); + const auto& currentLog = mModel->createLogFileName(); + mModel->setSource(QStringLiteral(":/logfiles/empty.txt")); + const auto& oldLog = mModel->createLogFileName(); - QCOMPARE(mModel->getLogFileNames().size(), 2); - mModel->removeOtherLogFiles(); - QCOMPARE(mModel->getLogFileNames().size(), 1); + QVERIFY(currentLog.contains(appName)); + QVERIFY(currentLog != oldLog); } diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_NotificationModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_NotificationModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_NotificationModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_NotificationModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -43,7 +43,7 @@ void test_OnNewLogMsg() { - QSignalSpy spy(mModel, &NotificationModel::fireLastTypeChanged); + QSignalSpy spy(mModel, &QAbstractItemModel::rowsInserted); const QLatin1String msg("message"); const QLoggingCategory develMode("developermode"); @@ -53,7 +53,6 @@ qCDebug(develMode).noquote() << msg; QTRY_VERIFY(mModel->mNotificationEntries.size() > i); QCOMPARE(mModel->mNotificationEntries.at(i).mText, msg); - QCOMPARE(mModel->mNotificationEntries.at(i).mType, QLatin1String(develMode.categoryName())); } const QLatin1String newMsg("new message"); @@ -62,7 +61,6 @@ QTRY_VERIFY(spy.count() > 20); QCOMPARE(mModel->mNotificationEntries.at(20).mText, newMsg); - QCOMPARE(mModel->mNotificationEntries.at(20).mType, QLatin1String(feedback.categoryName())); } @@ -73,12 +71,10 @@ 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("developermode"_L1); + QTest::newRow("RowNumberEqualsSize") << 2 << 2 << time << QVariant(); QTest::newRow("entriesFirstIndex2Text") << 3 << 22 << 5 << QVariant("message"_L1); QTest::newRow("indexOutOfRange") << 10 << 5 << time << QVariant(); } @@ -91,7 +87,7 @@ QFETCH(int, role); QFETCH(QVariant, output); - QSignalSpy spy(mModel, &NotificationModel::fireLastTypeChanged); + QSignalSpy spy(mModel, &QAbstractItemModel::rowsInserted); QModelIndex index = mModel->createIndex(row, 0); const QLatin1String msg("message"); diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_NumberModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_NumberModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_NumberModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_NumberModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -291,16 +291,16 @@ context->setCardConnection(connection); context->setLastPaceResult(CardReturnCode::INVALID_PIN); - QCOMPARE(mModel->getInputError(), tr("You have entered an incorrect, 6-digit ID card PIN.

You have 2 further attempts to enter the correct ID card PIN.")); + QCOMPARE(mModel->getInputError(), tr("You have entered an incorrect, 6-digit ID card PIN.

You have 2 further attempts to enter the correct ID card PIN.")); context->setLastPaceResult(CardReturnCode::INVALID_PIN_2); QCOMPARE(mModel->getInputError(), tr("You have entered an incorrect, 6-digit ID card PIN 2 times.

" - "For a 3rd attempt, the 6-digit Card Access Number (CAN) must be entered first. " + "For a 3rd attempt, the 6-digit Card Access Number (CAN) must be entered first. " "You can find your CAN in the bottom right on the front of your ID card.")); context->setLastPaceResult(CardReturnCode::INVALID_PIN_3); QCOMPARE(mModel->getInputError(), tr("You have entered an incorrect, 6-digit ID card PIN 3 times. Your ID card PIN is now blocked.

" - "To remove the block, the 10-digit PUK must be entered first. " + "To remove the block, the 10-digit PUK must be entered first. " "You can find the PUK in the bottom right next to the Transport PIN in the authority's letter.")); context->setLastPaceResult(CardReturnCode::INVALID_CAN); @@ -323,15 +323,15 @@ context->setCardConnection(connection); context->setLastPaceResult(CardReturnCode::INVALID_PIN); QCOMPARE(mModel->getInputError(), tr("You have entered an incorrect, 5-digit Transport PIN.

" - "You have 2 further attempts to enter the correct Transport PIN. " + "You have 2 further attempts to enter the correct Transport PIN. " "The 5-digit Transport PIN may be found on the bottom left of your PIN letter.")); context->setLastPaceResult(CardReturnCode::INVALID_PIN_2); QCOMPARE(mModel->getInputError(), tr("You have entered an incorrect, 5-digit Transport PIN 2 times.

" - "For a 3rd attempt, the 6-digit Card Access Number (CAN) must be entered first. " + "For a 3rd attempt, the 6-digit Card Access Number (CAN) must be entered first. " "You can find your CAN in the bottom right on the front of your ID card.")); context->setLastPaceResult(CardReturnCode::INVALID_PIN_3); QCOMPARE(mModel->getInputError(), tr("You have entered an incorrect, 5-digit Transport PIN 3 times, your Transport PIN is now blocked. " - "To remove the block, the 10-digit PUK must be entered first.")); + "To remove the block, the 10-digit PUK must be entered first.")); connectionThread.quit(); connectionThread.wait(); diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_PinResetInformationModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_PinResetInformationModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_PinResetInformationModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_PinResetInformationModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -6,6 +6,7 @@ #include "Env.h" #include "ProviderConfiguration.h" +#include "TestFileHelper.h" #include @@ -22,7 +23,25 @@ { Q_OBJECT + QTemporaryDir mTranslationDir; + private Q_SLOTS: + void initTestCase() + { + TestFileHelper::createTranslations(mTranslationDir.path()); + LanguageLoader::getInstance().setPath(mTranslationDir.path()); + } + + + void cleanup() + { + if (LanguageLoader::getInstance().isLoaded()) + { + LanguageLoader::getInstance().unload(); + } + } + + void test_fireUpdateOnTranslationChanged() { auto pinResetInformationModel = Env::getSingleton(); @@ -46,7 +65,45 @@ void test_PinResetUrl() { - QVERIFY(!Env::getSingleton()->getPinResetUrl().isEmpty()); + QCOMPARE(Env::getSingleton()->getPinResetUrl(), QUrl(QStringLiteral("https://servicesuche.bund.de/#/en"))); + + auto* config = Env::getSingleton(); + config->parseProviderConfiguration(":/updatable-files/supported-providers_prs.json"_L1); + QCOMPARE(Env::getSingleton()->getPinResetUrl(), QUrl(QStringLiteral("https://www.pin-ruecksetzbrief-bestellen.de/en"))); + + LanguageLoader::getInstance().load(QLocale::German); + QCOMPARE(Env::getSingleton()->getPinResetUrl(), QUrl(QStringLiteral("https://www.pin-ruecksetzbrief-bestellen.de"))); + + config->parseProviderConfiguration(":/updatable-files/supported-providers_no-prs.json"_L1); + QCOMPARE(Env::getSingleton()->getPinResetUrl(), QUrl(QStringLiteral("https://servicesuche.bund.de"))); + } + + + void test_PinResetActivationUrl() + { + QVERIFY(!Env::getSingleton()->getPinResetActivationUrl().isEmpty()); + } + + + void test_AdministrativeSearchUrl() + { + QCOMPARE(Env::getSingleton()->getAdministrativeSearchUrl(), QUrl(QStringLiteral("https://servicesuche.bund.de/#/en"))); + + LanguageLoader::getInstance().load(QLocale::German); + QCOMPARE(Env::getSingleton()->getAdministrativeSearchUrl(), QUrl(QStringLiteral("https://servicesuche.bund.de"))); + + } + + + void test_HasPinResetService() + { + auto* config = Env::getSingleton(); + + config->parseProviderConfiguration(":/updatable-files/supported-providers_no-prs.json"_L1); + QVERIFY(!Env::getSingleton()->hasPinResetService()); + + config->parseProviderConfiguration(":/updatable-files/supported-providers_prs.json"_L1); + QVERIFY(Env::getSingleton()->hasPinResetService()); } diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_ReaderModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_ReaderModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_ReaderModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_ReaderModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -116,14 +116,14 @@ mMockReaderConfiguration->clearReaderConfiguration(); ReaderModel readerModel; - QCOMPARE(readerModel.rowCount(), 0); + QCOMPARE(readerModel.rowCount(QModelIndex()), 0); } void test_settings() { ReaderModel readerModel; - QCOMPARE(readerModel.rowCount(), 0); + QCOMPARE(readerModel.rowCount(QModelIndex()), 0); } @@ -136,7 +136,7 @@ mUsbIds += UsbId(0x0C4B, 0x0501); // REINER SCT cyberJack RFID komfort ReaderModel readerModel; - QCOMPARE(readerModel.rowCount(), 1); + QCOMPARE(readerModel.rowCount(QModelIndex()), 1); const auto& index = readerModel.index(0, 0, QModelIndex()); const auto& htmlDescription = readerModel.data(index, ReaderModel::UserRoles::READER_HTML_DESCRIPTION).toString(); QVERIFY(htmlDescription.startsWith(tr("The smartcard service of your system is not reachable."))); @@ -148,7 +148,7 @@ mUsbIds += UsbId(0x1, 0x2); // Unknown ReaderModel readerModel; - QCOMPARE(readerModel.rowCount(), 0); + QCOMPARE(readerModel.rowCount(QModelIndex()), 0); } @@ -163,7 +163,7 @@ QModelIndex index; ReaderModel readerModel; - QCOMPARE(readerModel.rowCount(), 2); + QCOMPARE(readerModel.rowCount(QModelIndex()), 2); index = readerModel.index(0, 0, QModelIndex()); QCOMPARE(readerModel.data(index, ReaderModel::UserRoles::READER_HTML_DESCRIPTION).toString(), tr("The smartcard service of your system is not reachable.")); index = readerModel.index(1, 0, QModelIndex()); @@ -185,7 +185,7 @@ QModelIndex index; ReaderModel readerModel; - QCOMPARE(readerModel.rowCount(), 2); + QCOMPARE(readerModel.rowCount(QModelIndex()), 2); index = readerModel.index(0, 0, QModelIndex()); QCOMPARE(readerModel.data(index, ReaderModel::UserRoles::READER_HTML_DESCRIPTION).toString(), tr("Driver installed")); index = readerModel.index(1, 0, QModelIndex()); @@ -200,7 +200,7 @@ mReaderInfos += ReaderInfo("Governikus Special Reader"_L1, ReaderManagerPluginType::PCSC); ReaderModel readerModel; - QCOMPARE(readerModel.rowCount(), 1); + QCOMPARE(readerModel.rowCount(QModelIndex()), 1); } diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_ReleaseInformationModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_ReleaseInformationModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_ReleaseInformationModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_ReleaseInformationModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -67,32 +67,32 @@ QCOMPARE(mModel->allowRetry(), true); QCOMPARE(mSpyCurrentInformationChanged->count(), 0); QCOMPARE(mSpyUpdateInformationChanged->count(), 0); - QCOMPARE(mModel->getCurrentRelease()->rowCount(), 1); - QCOMPARE(mModel->getUpdateRelease()->rowCount(), 1); + QCOMPARE(mModel->getCurrentRelease()->rowCount(QModelIndex()), 1); + QCOMPARE(mModel->getUpdateRelease()->rowCount(QModelIndex()), 1); } void ensureChangedSignalOnCurrentReleaseInfoChanged() { - QCOMPARE(mModel->getCurrentRelease()->rowCount(), 1); + QCOMPARE(mModel->getCurrentRelease()->rowCount(QModelIndex()), 1); mModel->update(); QCOMPARE(mSpyCurrentInformationChanged->count(), 1); QCOMPARE(mSpyUpdateInformationChanged->count(), 0); - QCOMPARE(mModel->getCurrentRelease()->rowCount(), 11); - QCOMPARE(mModel->getUpdateRelease()->rowCount(), 1); + QCOMPARE(mModel->getCurrentRelease()->rowCount(QModelIndex()), 11); + QCOMPARE(mModel->getUpdateRelease()->rowCount(QModelIndex()), 1); } void ensureSignalOnUpdateReleaseInfoChanged() { - QCOMPARE(mModel->getUpdateRelease()->rowCount(), 1); + QCOMPARE(mModel->getUpdateRelease()->rowCount(QModelIndex()), 1); mReleaseInfoConfig->setUpdateVersion(VersionNumber("1.2.4"_L1)); QCOMPARE(mSpyUpdateInformationChanged->count(), 1); - QCOMPARE(mModel->getUpdateRelease()->rowCount(), 11); + QCOMPARE(mModel->getUpdateRelease()->rowCount(QModelIndex()), 11); } diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_RemoteDeviceModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_RemoteDeviceModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_RemoteDeviceModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_RemoteDeviceModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -2,35 +2,42 @@ * Copyright (c) 2018-2025 Governikus GmbH & Co. KG, Germany */ -#include "AppSettings.h" -#include "IfdDescriptor.h" #include "RemoteDeviceModel.h" + +#include "AppSettings.h" +#include "PortFile.h" #include "RemoteIfdClient.h" +#include "HttpServer.h" + #include + using namespace Qt::Literals::StringLiterals; using namespace governikus; + Q_DECLARE_METATYPE(RemoteDeviceModel::SettingsRemoteRoles) -class MockRemoteDeviceModel - : public RemoteDeviceModel + +class MockRemoteIfdClient + : public RemoteIfdClient { - [[nodiscard]] QList presentReaders() const override; + Q_OBJECT + + [[nodiscard]] QList> getAnnouncingRemoteDevices() const override; }; -QList MockRemoteDeviceModel::presentReaders() const + +QList> MockRemoteIfdClient::getAnnouncingRemoteDevices() const { const auto dName = QStringLiteral("new_name"); const auto dId = QByteArrayLiteral("myDeviceId"); - const auto disco = Discovery(dName, dId, 12345, {IfdVersion::Version::latest}, false); - const auto ifdDesc = IfdDescriptor(disco, QHostAddress(QStringLiteral("127.0.0.1"))); - const QSharedPointer remoteDeviceListEntry(new IfdListEntry(ifdDesc)); - remoteDeviceListEntry->setIfdDescriptor(ifdDesc); - return QList { - RemoteDeviceModelEntry {remoteDeviceListEntry} - }; + Discovery disco(dName, dId, 12345, {IfdVersion::Version::latest}, false); + disco.setAddresses({QHostAddress(QStringLiteral("127.0.0.1"))}); + const QSharedPointer remoteDeviceListEntry(new IfdListEntry(disco)); + remoteDeviceListEntry->setDiscovery(disco); + return {remoteDeviceListEntry}; } @@ -43,6 +50,12 @@ QString mName; private Q_SLOTS: + void initTestCase() + { + HttpServer::cPort = 0; + } + + void init() { mName = QStringLiteral("name<"); @@ -106,8 +119,8 @@ const QByteArray id = QByteArrayLiteral("id"); const QDateTime time(QDateTime::currentDateTime()); RemoteDeviceModelEntry entry1(name); - const IfdDescriptor descriptor = IfdDescriptor(); - QSharedPointer pointer(new IfdListEntry(descriptor)); + const Discovery disco((QJsonObject())); + QSharedPointer pointer(new IfdListEntry(disco)); RemoteDeviceModelEntry entry2(name, id, true, true, true, true, time, pointer); QVERIFY(!entry1.isSupported()); @@ -122,14 +135,14 @@ RemoteDeviceModelEntry entry1(name); QCOMPARE(entry1.getRemoteDeviceListEntry(), nullptr); - const IfdDescriptor defaultDescriptor = IfdDescriptor(); - QSharedPointer listEntry1(new IfdListEntry(defaultDescriptor)); + const Discovery defaultDisco((QJsonObject())); + QSharedPointer listEntry1(new IfdListEntry(defaultDisco)); RemoteDeviceModelEntry entry2(listEntry1); QCOMPARE(entry2.getRemoteDeviceListEntry(), listEntry1); - const Discovery discovery = Discovery(name, id, 11111, {IfdVersion::supported()}, true); - const IfdDescriptor descriptor = IfdDescriptor(discovery, QHostAddress::LocalHost, true); - QSharedPointer listEntry2(new IfdListEntry(descriptor)); + Discovery discovery(name, id, PortFile::cDefaultPort, {IfdVersion::supported()}, true); + discovery.setAddresses({QHostAddress(QHostAddress::LocalHost)}); + QSharedPointer listEntry2(new IfdListEntry(discovery)); RemoteDeviceModelEntry entry3(listEntry2); QCOMPARE(entry3.getRemoteDeviceListEntry(), listEntry2); QCOMPARE(entry3.getId(), id); @@ -201,7 +214,7 @@ entry.mSupported = true; entry.mIsPairing = true; #if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) - QCOMPARE(mModel->getStatus(entry), "Click to pair"_L1); + QCOMPARE(mModel->getStatus(entry), "Tap to pair"_L1); #else QCOMPARE(mModel->getStatus(entry), "Available"_L1); @@ -229,9 +242,9 @@ void test_GetRemoteDeviceListEntryModelIndex() { - const Discovery discovery = Discovery(QStringLiteral("entry 1"), QByteArrayLiteral("01"), 11111, {IfdVersion::supported()}, true); - const IfdDescriptor descriptor = IfdDescriptor(discovery, QHostAddress::LocalHost, true); - QSharedPointer listEntry(new IfdListEntry(descriptor)); + Discovery discovery(QStringLiteral("entry 1"), QByteArrayLiteral("01"), PortFile::cDefaultPort, {IfdVersion::supported()}, true); + discovery.setAddresses({QHostAddress(QHostAddress::LocalHost)}); + QSharedPointer listEntry(new IfdListEntry(discovery)); RemoteDeviceModelEntry entry1(listEntry); RemoteDeviceModelEntry entry2("entry 2"_L1); @@ -273,7 +286,7 @@ QFETCH(QVariant, output); QList readers; - QSharedPointer listEntry(new IfdListEntry(IfdDescriptor())); + QSharedPointer listEntry(new IfdListEntry(Discovery(QJsonObject()))); const RemoteDeviceModelEntry entry1("reader 1"_L1, "test id"_ba, true, false, true, false, QDateTime(QDate(2019, 5, 14), QTime(0, 0)), listEntry); const RemoteDeviceModelEntry entry2("reader 2"_L1); readers << entry1 << entry2; @@ -292,9 +305,9 @@ const auto dName = QStringLiteral("myDeviceName"); const auto dId = QByteArrayLiteral("myDeviceId"); - const auto disco = Discovery(dName, dId, 12345, {IfdVersion::Version::latest}, false); - const auto ifdDesc = IfdDescriptor(disco, QHostAddress(QStringLiteral("127.0.0.1"))); - const QSharedPointer remoteDeviceListEntry(new IfdListEntry(ifdDesc)); + Discovery disco(dName, dId, 12345, {IfdVersion::Version::latest}, false); + disco.setAddresses({QHostAddress(QStringLiteral("127.0.0.1"))}); + const QSharedPointer remoteDeviceListEntry(new IfdListEntry(disco)); mModel->addOrUpdateReader(RemoteDeviceModelEntry(remoteDeviceListEntry)); @@ -307,27 +320,32 @@ void test_ChangedName() { - MockRemoteDeviceModel model; + MockRemoteIfdClient remoteIfdClient; + Env::set(RemoteIfdClient::staticMetaObject, &remoteIfdClient); + + RemoteDeviceModel model; RemoteServiceSettings& settings = Env::getSingleton()->getRemoteServiceSettings(); settings.addTrustedCertificate(QSslCertificate()); const auto dName = QStringLiteral("old_name"); const auto dId = QByteArrayLiteral("myDeviceId"); - const auto disco = Discovery(dName, dId, 12345, {IfdVersion::Version::latest}, false); - const auto ifdDesc = IfdDescriptor(disco, QHostAddress(QStringLiteral("127.0.0.1"))); - const QSharedPointer remoteDeviceListEntry(new IfdListEntry(ifdDesc)); + Discovery disco(dName, dId, 12345, {IfdVersion::Version::latest}, false); + disco.setAddresses({QHostAddress(QStringLiteral("127.0.0.1"))}); + const QSharedPointer remoteDeviceListEntry(new IfdListEntry(disco)); model.mAllRemoteReaders.clear(); model.addOrUpdateReader(RemoteDeviceModelEntry(remoteDeviceListEntry)); const auto newName = QStringLiteral("new_name"); - const auto newDisco = Discovery(newName, dId, 12345, {IfdVersion::Version::latest}, false); - const auto newIfdDesc = IfdDescriptor(newDisco, QHostAddress(QStringLiteral("127.0.0.1"))); - remoteDeviceListEntry->setIfdDescriptor(newIfdDesc); + Discovery newDisco(newName, dId, 12345, {IfdVersion::Version::latest}, false); + newDisco.setAddresses({QHostAddress(QStringLiteral("127.0.0.1"))}); + remoteDeviceListEntry->setDiscovery(newDisco); const auto name = model.data(mModel->index(0), RemoteDeviceModel::SettingsRemoteRoles::REMOTE_DEVICE_NAME).toString(); QCOMPARE(name, QStringLiteral("new_name (was old_name)")); + + Env::clear(); } @@ -335,7 +353,7 @@ { QVERIFY(!Env::getSingleton()->isDetecting()); QTest::ignoreMessage(QtDebugMsg, "Starting Remote Device Detection"); - QTest::ignoreMessage(QtDebugMsg, "Bound on port: 24727"); + QTest::ignoreMessage(QtDebugMsg, QRegularExpression(QStringLiteral("Bound on port: "))); mModel->startDetection(); QVERIFY(Env::getSingleton()->isDetecting()); mModel->startDetection(); diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_SettingsModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_SettingsModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_SettingsModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_SettingsModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -21,6 +21,12 @@ Q_OBJECT private Q_SLOTS: + void init() + { + Env::getSingleton()->setPreferredTechnology(ReaderManagerPluginType::UNKNOWN); + } + + void testAutoRedirect() { auto* model = Env::getSingleton(); @@ -77,6 +83,10 @@ { const auto& settings = Env::getSingleton()->getGeneralSettings(); auto* model = Env::getSingleton(); + QCOMPARE(model->getDarkMode(), SettingsModel::ModeOption::AUTO); + + model->setDarkMode(SettingsModel::ModeOption::OFF); + QCOMPARE(settings.getDarkMode(), "OFF"_L1); QCOMPARE(model->getDarkMode(), SettingsModel::ModeOption::OFF); model->setDarkMode(SettingsModel::ModeOption::AUTO); @@ -145,17 +155,23 @@ auto* settingsModel = Env::getSingleton(); settingsModel->setTransportPinReminder(false); settingsModel->setRemindUserToClose(false); + auto remindRedirectSpy = QSignalSpy(settingsModel, &SettingsModel::fireRemindUserOfAutoRedirectChanged); + settingsModel->setRemindUserOfAutoRedirect(false); + QCOMPARE(remindRedirectSpy.count(), 1); settingsModel->hideFutureStoreFeedbackDialogs(); settingsModel->setStartupModule(UiModule::REMOTE_SERVICE); QVERIFY(!settingsModel->isTransportPinReminder()); QVERIFY(!settingsModel->isRemindUserToClose()); + QVERIFY(!settingsModel->isRemindUserOfAutoRedirect()); QVERIFY(!settingsModel->requestStoreFeedback()); QCOMPARE(settingsModel->getStartupModule(), UiModule::REMOTE_SERVICE); settingsModel->resetHideableDialogs(); QVERIFY(settingsModel->isTransportPinReminder()); QVERIFY(settingsModel->isRemindUserToClose()); + QVERIFY(settingsModel->isRemindUserOfAutoRedirect()); + QVERIFY(remindRedirectSpy.count() > 1); QVERIFY(settingsModel->requestStoreFeedback()); QCOMPARE(settingsModel->getStartupModule(), UiModule::ONBOARDING); } @@ -227,6 +243,74 @@ } + void test_manualUpdateCheck() + { + auto* settingsModel = Env::getSingleton(); + QSignalSpy spy(settingsModel, &SettingsModel::fireAppUpdateDataChanged); + Q_EMIT Env::getSingleton()->fireAppUpdateDataChanged(); + + QCOMPARE(spy.count(), 1); + QList arguments = spy.takeFirst(); + QVERIFY(arguments.at(0).toBool() == false); + spy.clear(); + + settingsModel->updateAppcast(); + Q_EMIT Env::getSingleton()->fireAppUpdateDataChanged(); + QCOMPARE(spy.count(), 1); + arguments = spy.takeFirst(); + QVERIFY(arguments.at(0).toBool() == true); + } + + + void test_isAutoRedirectAfterAuthentication() + { + auto* settings = Env::getSingleton(); + + QSignalSpy spy(settings, &SettingsModel::fireRemindUserOfAutoRedirectChanged); + bool initial = settings->isRemindUserOfAutoRedirect(); + bool newValue = !initial; + + settings->setRemindUserOfAutoRedirect(newValue); + QCOMPARE(spy.count(), 1); + QCOMPARE(settings->isRemindUserOfAutoRedirect(), newValue); + + settings->setRemindUserOfAutoRedirect(initial); + QCOMPARE(spy.count(), 2); + QCOMPARE(settings->isRemindUserOfAutoRedirect(), initial); + } + + + void test_onNfcStateChanged_data() + { + QTest::addColumn("nfcState"); + QTest::addColumn("initialPreferredTechnology"); + QTest::addColumn("finalPreferredTechnology"); + + QTest::addRow("NFC ready and selected") << ApplicationModel::NfcState::READY << ReaderManagerPluginType::NFC << ReaderManagerPluginType::NFC; + QTest::addRow("NFC inactive and selected") << ApplicationModel::NfcState::INACTIVE << ReaderManagerPluginType::NFC << ReaderManagerPluginType::NFC; + QTest::addRow("NFC disabled and selected") << ApplicationModel::NfcState::DISABLED << ReaderManagerPluginType::NFC << ReaderManagerPluginType::NFC; + QTest::addRow("NFC unavailable and selected") << ApplicationModel::NfcState::UNAVAILABLE << ReaderManagerPluginType::NFC << ReaderManagerPluginType::REMOTE_IFD; + QTest::addRow("NFC unavailable and simulator selected") << ApplicationModel::NfcState::UNAVAILABLE << ReaderManagerPluginType::SIMULATOR << ReaderManagerPluginType::SIMULATOR; + QTest::addRow("NFC unavailable and remote ifd selected") << ApplicationModel::NfcState::UNAVAILABLE << ReaderManagerPluginType::REMOTE_IFD << ReaderManagerPluginType::REMOTE_IFD; + } + + + void test_onNfcStateChanged() + { + QFETCH(ApplicationModel::NfcState, nfcState); + QFETCH(ReaderManagerPluginType, initialPreferredTechnology); + QFETCH(ReaderManagerPluginType, finalPreferredTechnology); + + auto* settings = Env::getSingleton(); + settings->setPreferredTechnology(initialPreferredTechnology); + QSignalSpy spy(settings, &SettingsModel::firePreferredTechnologyChanged); + + settings->onNfcStateChanged(nfcState); + QCOMPARE(settings->getPreferredTechnology(), finalPreferredTechnology); + QCOMPARE(spy.count(), 1); + } + + }; QTEST_GUILESS_MAIN(test_SettingsModel) diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_UiPluginQml.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_UiPluginQml.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_UiPluginQml.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_UiPluginQml.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -162,6 +162,23 @@ } + void test_setFontWeightAdjustment() + { + UiPluginQml plugin; + QSignalSpy spy(&plugin, &UiPluginQml::fireFontWeightAdjustmentChanged); + + const auto initialValue = plugin.getFontWeightAdjustment(); + + plugin.setFontWeightAdjustment(initialValue); + QTRY_COMPARE(spy.count(), 0); + + const auto newValue = 42; + plugin.setFontWeightAdjustment(newValue); + QTRY_COMPARE(spy.count(), 1); + QCOMPARE(plugin.getFontWeightAdjustment(), newValue); + } + + void test_darkModeEnabled_data() { QTest::addColumn("userDarkMode"); @@ -199,7 +216,7 @@ } - void test_overridePlattform() + void test_overridePlatform() { QCOMPARE(UiPluginQml::getOverridePlatform(), QString()); qputenv("OVERRIDE_PLATFORM", "dummy"_ba); @@ -214,9 +231,7 @@ QQmlEngine engine; QCOMPARE(UiPluginQml::adjustQmlImportPath(&engine), primaryPrefix); -#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) QVERIFY(engine.importPathList().contains(primaryPrefix)); -#endif qputenv("OVERRIDE_PLATFORM", "platform"_ba); QTest::ignoreMessage(QtDebugMsg, "Override platform: \"platform\""); @@ -246,6 +261,48 @@ QCOMPARE(spy.count(), 2); } + + void test_a11yButtonShape() + { + UiPluginQml plugin; + QSignalSpy spy(&plugin, &UiPluginModel::fireA11yButtonShapeActiveChanged); + + QVERIFY(!plugin.isA11yButtonShapeActive()); + + plugin.setA11yButtonShapeActive(false); + QVERIFY(!plugin.isA11yButtonShapeActive()); + QCOMPARE(spy.count(), 0); + + plugin.setA11yButtonShapeActive(true); + QVERIFY(plugin.isA11yButtonShapeActive()); + QCOMPARE(spy.count(), 1); + + plugin.setA11yButtonShapeActive(false); + QVERIFY(!plugin.isA11yButtonShapeActive()); + QCOMPARE(spy.count(), 2); + } + + + void test_a11yOnOffSwitchLabel() + { + UiPluginQml plugin; + QSignalSpy spy(&plugin, &UiPluginModel::fireA11yOnOffSwitchLabelActiveChanged); + + QVERIFY(!plugin.isA11yOnOffSwitchLabelActive()); + + plugin.setA11yOnOffSwitchLabelActive(false); + QVERIFY(!plugin.isA11yOnOffSwitchLabelActive()); + QCOMPARE(spy.count(), 0); + + plugin.setA11yOnOffSwitchLabelActive(true); + QVERIFY(plugin.isA11yOnOffSwitchLabelActive()); + QCOMPARE(spy.count(), 1); + + plugin.setA11yOnOffSwitchLabelActive(false); + QVERIFY(!plugin.isA11yOnOffSwitchLabelActive()); + QCOMPARE(spy.count(), 2); + } + }; diff -Nru ausweisapp2-2.3.1/test/qt/ui/qml/test_WorkflowModel.cpp ausweisapp2-2.4.0/test/qt/ui/qml/test_WorkflowModel.cpp --- ausweisapp2-2.3.1/test/qt/ui/qml/test_WorkflowModel.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/qml/test_WorkflowModel.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -204,6 +204,35 @@ } + void test_getStatusCodeDisplayString_data() + { + QTest::addColumn>("context"); + QTest::addColumn("statusCode"); + QTest::addColumn("result"); + + QTest::addRow("No context") << QSharedPointer(nullptr) << GlobalStatus::Code::No_Error << "Error code: Unknown_Error"; + QTest::addRow("Any error") << QSharedPointer(new TestWorkflowContext()) << GlobalStatus::Code::Card_Communication_Error << "Error code: Card_Communication_Error"; + QTest::addRow("No error") << QSharedPointer(new TestWorkflowContext()) << GlobalStatus::Code::No_Error << "Error code: No_Error"; + } + + + void test_getStatusCodeDisplayString() + { + QFETCH(QSharedPointer, context); + QFETCH(GlobalStatus::Code, statusCode); + QFETCH(QString, result); + + auto* const model = Env::getSingleton(); + if (context) + { + context->setStatus(GlobalStatus(statusCode)); + } + model->resetWorkflowContext(context); + + QCOMPARE(model->getStatusCodeDisplayString(), result); + } + + }; QTEST_MAIN(test_WorkflowModel) diff -Nru ausweisapp2-2.3.1/test/qt/ui/webservice/test_UiPluginWebService.cpp ausweisapp2-2.4.0/test/qt/ui/webservice/test_UiPluginWebService.cpp --- ausweisapp2-2.3.1/test/qt/ui/webservice/test_UiPluginWebService.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/ui/webservice/test_UiPluginWebService.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -43,7 +43,8 @@ bool containsHeader(const QSharedPointer& pReply, QByteArrayView pHeader) { - for (const QByteArray& header : pReply->rawHeaderList()) + const auto& list = pReply->rawHeaderList(); + for (const QByteArray& header : list) { if (header.toLower() == pHeader) { @@ -187,9 +188,7 @@ void authentication() { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) QSKIP("QTBUG-130666"); -#endif connect(mUi.data(), &UiPlugin::fireWorkflowRequested, mUi.data(), &UiPlugin::onWorkflowStarted); // fake AppController QTest::ignoreMessage(QtDebugMsg, "Request type: authentication"); @@ -349,7 +348,7 @@ QTest::newRow("timeout") << 0 << static_cast(UiPluginWebService::ExistingAppResult::SHOWUI_TIMEOUT); QTest::newRow("no-proxy") << 200 << static_cast(UiPluginWebService::ExistingAppResult::SHOWUI_SUCCEED); QTest::newRow("proxy-failed") << 502 << static_cast(UiPluginWebService::ExistingAppResult::REBIND_FAILED); - QTest::newRow("proxy-succedd") << 502 << static_cast(UiPluginWebService::ExistingAppResult::REBIND_SUCCEED); + QTest::newRow("proxy-succeeded") << 502 << static_cast(UiPluginWebService::ExistingAppResult::REBIND_SUCCEED); } diff -Nru ausweisapp2-2.3.1/test/qt/workflows/context/test_AuthContext.cpp ausweisapp2-2.4.0/test/qt/workflows/context/test_AuthContext.cpp --- ausweisapp2-2.3.1/test/qt/workflows/context/test_AuthContext.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/context/test_AuthContext.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -233,7 +233,9 @@ const QByteArray session("session"); context.setSslSession(session); + context.setSslSessionPsk(session); QCOMPARE(context.getSslSession(), session); + QCOMPARE(context.getSslSessionPsk(), session); } diff -Nru ausweisapp2-2.3.1/test/qt/workflows/ifd/test_StateEnterNewPacePinIfd.cpp ausweisapp2-2.4.0/test/qt/workflows/ifd/test_StateEnterNewPacePinIfd.cpp --- ausweisapp2-2.3.1/test/qt/workflows/ifd/test_StateEnterNewPacePinIfd.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/ifd/test_StateEnterNewPacePinIfd.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,14 +4,21 @@ #include "states/StateEnterNewPacePinIfd.h" +#include "ReaderManager.h" #include "context/IfdServiceContext.h" #include "states/StateBuilder.h" +#include "MockCardConnection.h" #include "MockIfdServer.h" +#include "MockReaderManagerPlugin.h" #include +Q_IMPORT_PLUGIN(MockReaderManagerPlugin) + + +using namespace Qt::Literals::StringLiterals; using namespace governikus; @@ -59,6 +66,36 @@ } + void removeCard() + { + auto* readerManager = Env::getSingleton(); + QSignalSpy spy(readerManager, &ReaderManager::fireInitialized); + readerManager->init(); + QTRY_COMPARE(spy.count(), 1); // clazy:exclude=qstring-allocations + + QSignalSpy cardRemoved(readerManager, &ReaderManager::fireCardRemoved); + + const auto& context = mState->getContext(); + MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().getReader("MockReader"_L1); + mState->onEntry(nullptr); + + context->setReaderName("MockWrongReader"_L1); + context->setCardConnection(QSharedPointer::create()); + Q_EMIT reader->fireCardRemoved(reader->getReaderInfo()); + QTRY_COMPARE(cardRemoved.count(), 1); + QVERIFY(!context->getCardConnection().isNull()); + + context->setReaderName("MockReader"_L1); + QTest::ignoreMessage(QtDebugMsg, "Card was removed while waiting for user input. Resetting card connection"); + Q_EMIT reader->fireCardRemoved(reader->getReaderInfo()); + QTRY_COMPARE(cardRemoved.count(), 2); + QVERIFY(context->getCardConnection().isNull()); + + readerManager->shutdown(); + } + + }; QTEST_GUILESS_MAIN(test_StateEnterNewPacePinIfd) diff -Nru ausweisapp2-2.3.1/test/qt/workflows/ifd/test_StateEnterPacePasswordIfd.cpp ausweisapp2-2.4.0/test/qt/workflows/ifd/test_StateEnterPacePasswordIfd.cpp --- ausweisapp2-2.3.1/test/qt/workflows/ifd/test_StateEnterPacePasswordIfd.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/ifd/test_StateEnterPacePasswordIfd.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,14 +4,21 @@ #include "states/StateEnterPacePasswordIfd.h" +#include "ReaderManager.h" #include "context/IfdServiceContext.h" #include "states/StateBuilder.h" +#include "MockCardConnection.h" #include "MockIfdServer.h" +#include "MockReaderManagerPlugin.h" #include +Q_IMPORT_PLUGIN(MockReaderManagerPlugin) + + +using namespace Qt::Literals::StringLiterals; using namespace governikus; @@ -59,6 +66,36 @@ } + void removeCard() + { + auto* readerManager = Env::getSingleton(); + QSignalSpy spy(readerManager, &ReaderManager::fireInitialized); + readerManager->init(); + QTRY_COMPARE(spy.count(), 1); // clazy:exclude=qstring-allocations + + QSignalSpy cardRemoved(readerManager, &ReaderManager::fireCardRemoved); + + const auto& context = mState->getContext(); + MockReaderManagerPlugin::getInstance().addReader("MockReader"_L1); + const auto& reader = MockReaderManagerPlugin::getInstance().getReader("MockReader"_L1); + mState->onEntry(nullptr); + + context->setReaderName("MockWrongReader"_L1); + context->setCardConnection(QSharedPointer::create()); + Q_EMIT reader->fireCardRemoved(reader->getReaderInfo()); + QTRY_COMPARE(cardRemoved.count(), 1); + QVERIFY(!context->getCardConnection().isNull()); + + context->setReaderName("MockReader"_L1); + QTest::ignoreMessage(QtDebugMsg, "Card was removed while waiting for user input. Resetting card connection"); + Q_EMIT reader->fireCardRemoved(reader->getReaderInfo()); + QTRY_COMPARE(cardRemoved.count(), 2); + QVERIFY(context->getCardConnection().isNull()); + + readerManager->shutdown(); + } + + }; QTEST_GUILESS_MAIN(test_StateEnterPacePasswordIfd) diff -Nru ausweisapp2-2.3.1/test/qt/workflows/ifd/test_StateProcessIfdMessages.cpp ausweisapp2-2.4.0/test/qt/workflows/ifd/test_StateProcessIfdMessages.cpp --- ausweisapp2-2.3.1/test/qt/workflows/ifd/test_StateProcessIfdMessages.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/ifd/test_StateProcessIfdMessages.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -143,10 +143,10 @@ state.onDisplayTextChanged(QStringLiteral("dummy text")); QCOMPARE(spy.count(), 1); - QCOMPARE(context->getDisplayText(), QStringLiteral("dummy text")); + QCOMPARE(context->getDisplayText(), QLatin1String("dummy text")); state.onCardDisconnected(); QCOMPARE(spy.count(), 2); - QCOMPARE(context->getDisplayText(), QStringLiteral("")); + QCOMPARE(context->getDisplayText(), QLatin1String("")); } diff -Nru ausweisapp2-2.3.1/test/qt/workflows/paos/retrieve/test_DidAuthenticateEac1.cpp ausweisapp2-2.4.0/test/qt/workflows/paos/retrieve/test_DidAuthenticateEac1.cpp --- ausweisapp2-2.3.1/test/qt/workflows/paos/retrieve/test_DidAuthenticateEac1.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/paos/retrieve/test_DidAuthenticateEac1.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -39,7 +39,7 @@ QTest::addColumn("filename"); const QStringList sel = QStringList({"DIDAuthenticateEAC1*.xml"_L1}); - QStringList files = QDir(":/paos"_L1).entryList(sel, QDir::Files); + const QStringList files = QDir(":/paos"_L1).entryList(sel, QDir::Files); for (const auto& file : files) { QTest::newRow(file.toLatin1().data()) << ":/paos/"_L1 + file; diff -Nru ausweisapp2-2.3.1/test/qt/workflows/selfauth/test_SelfAuthenticationData.cpp ausweisapp2-2.4.0/test/qt/workflows/selfauth/test_SelfAuthenticationData.cpp --- ausweisapp2-2.3.1/test/qt/workflows/selfauth/test_SelfAuthenticationData.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/selfauth/test_SelfAuthenticationData.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -66,8 +66,8 @@ void dateFormat() { SelfAuthenticationData::OrderedSelfData expected; - expected << qMakePair("Date of birth"_L1, "xx.xx.1946"_L1); - expected << qMakePair("Date of expiry"_L1, "xx.11.2029"_L1); + expected << std::make_pair("Date of birth"_L1, "xx.xx.1946"_L1); + expected << std::make_pair("Valid until"_L1, "xx.11.2029"_L1); const auto& data = TestFileHelper::readFile(":/self/SelfAuthenticationDataDateFormat.json"_L1); const SelfAuthenticationData selfAuthenticationData(data); @@ -80,20 +80,20 @@ void orderedSelfData() { SelfAuthenticationData::OrderedSelfData expected; - expected << qMakePair("Family name"_L1, QStringLiteral("von Drebenbusch-Dalgo\u00DFen")); - expected << qMakePair("Birth name"_L1, QStringLiteral("Wei\u00dF")); - expected << qMakePair("Given name(s)"_L1, QStringLiteral("Hans-G\u00FCnther")); - expected << qMakePair("Doctoral degree"_L1, "Dr.eh.Dr."_L1); - expected << qMakePair("Date of birth"_L1, "25.01.1946"_L1); - expected << qMakePair("Place of birth"_L1, "BREMERHAVEN"_L1); - expected << qMakePair("Address"_L1, "WEG NR.12 8E"_L1); - expected << qMakePair(QString(), "22043 HAMBURG"_L1); - expected << qMakePair(QString(), "D"_L1); - expected << qMakePair("Document type"_L1, "ID"_L1); - expected << qMakePair("Nationality"_L1, "D"_L1); - expected << qMakePair("Religious / artistic name"_L1, QStringLiteral("Freiherr zu M\u00F6ckern-Windensberg")); - expected << qMakePair("Issuing country"_L1, "D"_L1); - expected << qMakePair("Date of expiry"_L1, "30.11.2029"_L1); + expected << std::make_pair("Family name"_L1, QStringLiteral("von Drebenbusch-Dalgo\u00DFen")); + expected << std::make_pair("Birth name"_L1, QStringLiteral("Wei\u00dF")); + expected << std::make_pair("Given name(s)"_L1, QStringLiteral("Hans-G\u00FCnther")); + expected << std::make_pair("Doctoral degree"_L1, "Dr.eh.Dr."_L1); + expected << std::make_pair("Date of birth"_L1, "25.01.1946"_L1); + expected << std::make_pair("Place of birth"_L1, "BREMERHAVEN"_L1); + expected << std::make_pair("Address"_L1, "WEG NR.12 8E"_L1); + expected << std::make_pair(QString(), "22043 HAMBURG"_L1); + expected << std::make_pair(QString(), "D"_L1); + expected << std::make_pair("Document type"_L1, "ID"_L1); + expected << std::make_pair("Nationality"_L1, "D"_L1); + expected << std::make_pair("Religious / artistic name"_L1, QStringLiteral("Freiherr zu M\u00F6ckern-Windensberg")); + expected << std::make_pair("Issuing country"_L1, "D"_L1); + expected << std::make_pair("Valid until"_L1, "30.11.2029"_L1); const auto& data = TestFileHelper::readFile(":/self/SelfAuthenticationDataID.json"_L1); SelfAuthenticationData selfAuthenticationData(data); @@ -119,7 +119,7 @@ QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::AcademicTitle), QString()); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::BirthName), QString()); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::DateOfBirth), "1981-06-17+02:00"_L1); - QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfBirth), "FRANKFURT (ODER)"_L1); + QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfBirth), "FRANKFURT (ODER)"_L1); // codespell:ignore QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceStreet), QStringLiteral("EHM-WELK-STRA\u00dfE 33")); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceCity), QStringLiteral("L\u00dcBBENAU/SPREEWALD")); QCOMPARE(selfAuthenticationData.getValue(SelfAuthData::PlaceOfResidenceCountry), "D"_L1); diff -Nru ausweisapp2-2.3.1/test/qt/workflows/states/test_StateChangePin.cpp ausweisapp2-2.4.0/test/qt/workflows/states/test_StateChangePin.cpp --- ausweisapp2-2.3.1/test/qt/workflows/states/test_StateChangePin.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/states/test_StateChangePin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -163,7 +163,7 @@ if (fireContinue > 0) { - QCOMPARE(context->getSuccessMessage(), tr("You have successfully changed your ID card PIN.")); + QCOMPARE(context->getSuccessMessage(), tr("You have successfully changed your ID card PIN.") + QStringLiteral("
") + tr("You may now remove your ID card from the device.")); } QCOMPARE(spyContinue.count(), fireContinue); QCOMPARE(spyAbort.count(), fireAbort); diff -Nru ausweisapp2-2.3.1/test/qt/workflows/states/test_StateCheckRefreshAddress.cpp ausweisapp2-2.4.0/test/qt/workflows/states/test_StateCheckRefreshAddress.cpp --- ausweisapp2-2.3.1/test/qt/workflows/states/test_StateCheckRefreshAddress.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/states/test_StateCheckRefreshAddress.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -261,24 +261,26 @@ { QTest::addColumn("networkError"); QTest::addColumn("status"); - QTest::addColumn("failureCode"); + QTest::addColumn>("failureCode"); QTest::addColumn("statusCode"); QTest::addColumn("redirectUrl"); QTest::addColumn("developerMode"); - QTest::newRow("http service unavailable") << QNetworkReply::NetworkError::ServiceUnavailableError << GlobalStatus::Code::Network_ServiceUnavailable << FailureCode::Reason::Check_Refresh_Address_Service_Unavailable << 503 << QUrl("http://governikus.com/"_L1) << false; - QTest::newRow("http internal server error") << QNetworkReply::NetworkError::InternalServerError << GlobalStatus::Code::Network_ServerError << FailureCode::Reason::Check_Refresh_Address_Server_Error << 500 << QUrl("http://governikus.com/"_L1) << false; - QTest::newRow("http not found") << QNetworkReply::NetworkError::ContentNotFoundError << GlobalStatus::Code::Network_ClientError << FailureCode::Reason::Check_Refresh_Address_Client_Error << 404 << QUrl("https://governikus.com/"_L1) << false; - QTest::newRow("http other error") << QNetworkReply::NetworkError::ProtocolUnknownError << GlobalStatus::Code::Network_Other_Error << FailureCode::Reason::Check_Refresh_Address_Unknown_Network_Error << 304 << QUrl("http://governikus.com/"_L1) << false; - QTest::newRow("timeout") << QNetworkReply::NetworkError::TimeoutError << GlobalStatus::Code::Network_TimeOut << FailureCode::Reason::Check_Refresh_Address_Service_Timeout << 2 << QUrl() << false; - QTest::newRow("proxy error") << QNetworkReply::NetworkError::ProxyNotFoundError << GlobalStatus::Code::Network_Proxy_Error << FailureCode::Reason::Check_Refresh_Address_Proxy_Error << 0 << QUrl("test"_L1) << false; - QTest::newRow("ssl error") << QNetworkReply::NetworkError::SslHandshakeFailedError << GlobalStatus::Code::Network_Ssl_Establishment_Error << FailureCode::Reason::Check_Refresh_Address_Fatal_Tls_Error_After_Reply << 1 << QUrl("https://governikus.com/"_L1) << false; - QTest::newRow("other error") << QNetworkReply::NetworkError::OperationCanceledError << GlobalStatus::Code::Network_Other_Error << FailureCode::Reason::Check_Refresh_Address_Unknown_Network_Error << 2 << QUrl("https://governikus.com/"_L1) << false; - QTest::newRow("no error unexpected status") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Expected_Redirect << FailureCode::Reason::Check_Refresh_Address_Invalid_Http_Response << 2 << QUrl("https://governikus.com/"_L1) << false; - QTest::newRow("no error empty url") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Empty_Redirect_Url << FailureCode::Reason::Check_Refresh_Address_Empty << 302 << QUrl() << false; - QTest::newRow("no error invalid url") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Malformed_Redirect_Url << FailureCode::Reason::Check_Refresh_Address_Invalid_Url << 302 << QUrl("://://"_L1) << false; - QTest::newRow("no error http") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Invalid_Scheme << FailureCode::Reason::Check_Refresh_Address_No_Https_Scheme << 302 << QUrl("http://governikus.com/"_L1) << false; - QTest::newRow("no error http developer mode") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::No_Error << FailureCode::Reason::Check_Refresh_Address_No_Https_Scheme << 302 << QUrl("http://governikus.com/"_L1) << true; + QTest::newRow("no error") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::No_Error << std::optional() << 302 << QUrl("https://governikus.com/index.html"_L1) << false; + QTest::newRow("no error relative") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::No_Error << std::optional() << 302 << QUrl("index.html"_L1) << false; + QTest::newRow("http service unavailable") << QNetworkReply::NetworkError::ServiceUnavailableError << GlobalStatus::Code::Network_ServiceUnavailable << std::optional(FailureCode::Reason::Check_Refresh_Address_Service_Unavailable) << 503 << QUrl("http://governikus.com/"_L1) << false; + QTest::newRow("http internal server error") << QNetworkReply::NetworkError::InternalServerError << GlobalStatus::Code::Network_ServerError << std::optional(FailureCode::Reason::Check_Refresh_Address_Server_Error) << 500 << QUrl("http://governikus.com/"_L1) << false; + QTest::newRow("http not found") << QNetworkReply::NetworkError::ContentNotFoundError << GlobalStatus::Code::Network_ClientError << std::optional(FailureCode::Reason::Check_Refresh_Address_Client_Error) << 404 << QUrl("https://governikus.com/"_L1) << false; + QTest::newRow("http other error") << QNetworkReply::NetworkError::ProtocolUnknownError << GlobalStatus::Code::Network_Other_Error << std::optional(FailureCode::Reason::Check_Refresh_Address_Unknown_Network_Error) << 304 << QUrl("http://governikus.com/"_L1) << false; + QTest::newRow("timeout") << QNetworkReply::NetworkError::TimeoutError << GlobalStatus::Code::Network_TimeOut << std::optional(FailureCode::Reason::Check_Refresh_Address_Service_Timeout) << 2 << QUrl() << false; + QTest::newRow("proxy error") << QNetworkReply::NetworkError::ProxyNotFoundError << GlobalStatus::Code::Network_Proxy_Error << std::optional(FailureCode::Reason::Check_Refresh_Address_Proxy_Error) << 0 << QUrl("test"_L1) << false; + QTest::newRow("ssl error") << QNetworkReply::NetworkError::SslHandshakeFailedError << GlobalStatus::Code::Network_Ssl_Establishment_Error << std::optional(FailureCode::Reason::Check_Refresh_Address_Fatal_Tls_Error_After_Reply) << 1 << QUrl("https://governikus.com/"_L1) << false; + QTest::newRow("other error") << QNetworkReply::NetworkError::OperationCanceledError << GlobalStatus::Code::Network_Other_Error << std::optional(FailureCode::Reason::Check_Refresh_Address_Unknown_Network_Error) << 2 << QUrl("https://governikus.com/"_L1) << false; + QTest::newRow("no error unexpected status") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Expected_Redirect << std::optional(FailureCode::Reason::Check_Refresh_Address_Invalid_Http_Response) << 2 << QUrl("https://governikus.com/"_L1) << false; + QTest::newRow("no error empty url") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Empty_Redirect_Url << std::optional(FailureCode::Reason::Check_Refresh_Address_Empty) << 302 << QUrl() << false; + QTest::newRow("no error invalid url") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Malformed_Redirect_Url << std::optional(FailureCode::Reason::Check_Refresh_Address_Invalid_Url) << 302 << QUrl("://://"_L1) << false; + QTest::newRow("no error http") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::Workflow_Network_Invalid_Scheme << std::optional(FailureCode::Reason::Check_Refresh_Address_No_Https_Scheme) << 302 << QUrl("http://governikus.com/"_L1) << false; + QTest::newRow("no error http developer mode") << QNetworkReply::NetworkError::NoError << GlobalStatus::Code::No_Error << std::optional(FailureCode::Reason::Check_Refresh_Address_No_Https_Scheme) << 302 << QUrl("http://governikus.com/"_L1) << true; } @@ -286,7 +288,7 @@ { QFETCH(QNetworkReply::NetworkError, networkError); QFETCH(GlobalStatus::Code, status); - QFETCH(FailureCode::Reason, failureCode); + QFETCH(std::optional, failureCode); QFETCH(int, statusCode); QFETCH(QUrl, redirectUrl); QFETCH(bool, developerMode); @@ -298,6 +300,7 @@ const QByteArray headerName("name"); const QByteArray value("value"); + reply->setRequest(QNetworkRequest(QUrl("https://www.foo.bar/refresh"_L1))); reply->setRawHeader(headerName, value); reply->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, QVariant(statusCode)); reply->setAttribute(QNetworkRequest::RedirectionTargetAttribute, QVariant(redirectUrl)); diff -Nru ausweisapp2-2.3.1/test/qt/workflows/states/test_StateDidAuthenticateEac2.cpp ausweisapp2-2.4.0/test/qt/workflows/states/test_StateDidAuthenticateEac2.cpp --- ausweisapp2-2.3.1/test/qt/workflows/states/test_StateDidAuthenticateEac2.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/states/test_StateDidAuthenticateEac2.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -20,8 +20,8 @@ public: explicit MockDidAuthenticateEAC2Command(const QSharedPointer& pCardConnectionWorker, const CVCertificateChain& pCvcChain, - const QByteArray& pEphermalPublicKeyAsHex, const QByteArray& pSignatureAsHex, const QByteArray& pAuthenticatedAuxiliaryDataAsBinary) - : DidAuthenticateEAC2Command(pCardConnectionWorker, pCvcChain, pEphermalPublicKeyAsHex, pSignatureAsHex, pAuthenticatedAuxiliaryDataAsBinary, QByteArray()) + const QByteArray& pEphemeralPublicKeyAsHex, const QByteArray& pSignatureAsHex, const QByteArray& pAuthenticatedAuxiliaryDataAsBinary) + : DidAuthenticateEAC2Command(pCardConnectionWorker, pCvcChain, pEphemeralPublicKeyAsHex, pSignatureAsHex, pAuthenticatedAuxiliaryDataAsBinary, QByteArray()) { } diff -Nru ausweisapp2-2.3.1/test/qt/workflows/states/test_StateGenericProviderCommunication.cpp ausweisapp2-2.4.0/test/qt/workflows/states/test_StateGenericProviderCommunication.cpp --- ausweisapp2-2.3.1/test/qt/workflows/states/test_StateGenericProviderCommunication.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/states/test_StateGenericProviderCommunication.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -9,9 +9,10 @@ #include "MockNetworkManager.h" #include -#include #include +#include + Q_DECLARE_LOGGING_CATEGORY(network) @@ -19,7 +20,7 @@ using namespace governikus; -using Pair = QPair; +using Pair = std::pair; Q_DECLARE_METATYPE(QList) diff -Nru ausweisapp2-2.3.1/test/qt/workflows/states/test_StateGenericSendReceive.cpp ausweisapp2-2.4.0/test/qt/workflows/states/test_StateGenericSendReceive.cpp --- ausweisapp2-2.3.1/test/qt/workflows/states/test_StateGenericSendReceive.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/states/test_StateGenericSendReceive.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -8,16 +8,19 @@ #include "MockNetworkManager.h" #include "TestAuthContext.h" +#include "VolatileSettings.h" #include -#include #include +#include + + using namespace Qt::Literals::StringLiterals; using namespace governikus; Q_DECLARE_METATYPE(QSharedPointer) -using Pair = QPair; +using Pair = std::pair; Q_DECLARE_METATYPE(QList) class test_StateGenericSendReceive @@ -67,7 +70,7 @@ } - void checkServerAdress_data() + void checkServerAddress_data() { QTest::addColumn("personalization"); QTest::addColumn("url"); @@ -77,7 +80,7 @@ } - void checkServerAdress() + void checkServerAddress() { QFETCH(bool, personalization); QFETCH(QUrl, url); @@ -391,6 +394,78 @@ } + void checkAndSaveSessionResumptionPsk_data() + { + QTest::addColumn("sessionTicket"); + QTest::addColumn("resumptionSessionTicket"); + + QTest::addRow("Empty tickets") << QByteArray() << QByteArray(); + QTest::addRow("Matching tickets") << QByteArray("1234") << QByteArray("1234"); + QTest::addRow("Mismatching tickets") << QByteArray("1234") << QByteArray(); + } + + + void checkAndSaveSessionResumptionPsk() + { + QFETCH(QByteArray, sessionTicket); + QFETCH(QByteArray, resumptionSessionTicket); + + mState->mReply = QSharedPointer::create(); + + mAuthContext->setSslSessionPsk(sessionTicket); + QSslConfiguration sslConfig; + sslConfig.setSessionTicket(resumptionSessionTicket); + + QCOMPARE(mState->checkAndSaveSessionResumption(sslConfig).has_value(), false); + } + + + void checkAndSaveSessionResumptionAttachedEid_data() + { + QTest::addColumn("tcTokenSessionTicket"); + QTest::addColumn("resumptionSessionTicket"); + QTest::addColumn("developerMode"); + QTest::addColumn>("failureCode"); + + QTest::addRow("Empty tickets") << QByteArray() << QByteArray() << false << std::optional(FailureCode(FailureCode::Reason::Generic_Send_Receive_Session_Resumption_Failed)); + QTest::addRow("Matching tickets") << QByteArray("1234") << QByteArray("1234") << false << std::optional(); + QTest::addRow("Mismatching tickets") << QByteArray("1234") << QByteArray() << false << std::optional(FailureCode(FailureCode::Reason::Generic_Send_Receive_Session_Resumption_Failed)); + QTest::addRow("Empty tickets - Developer Mode") << QByteArray() << QByteArray() << true << std::optional(); + QTest::addRow("Matching tickets - Developer Mode") << QByteArray("1234") << QByteArray("1234") << true << std::optional(); + QTest::addRow("Mismatching tickets - Developer Mode") << QByteArray("1234") << QByteArray() << true << std::optional(); + } + + + void checkAndSaveSessionResumptionAttachedEid() + { + QFETCH(QByteArray, tcTokenSessionTicket); + QFETCH(QByteArray, resumptionSessionTicket); + QFETCH(bool, developerMode); + QFETCH(std::optional, failureCode); + + 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 " + ""); + const QSharedPointer token(new TcToken(data)); + mAuthContext->setTcToken(token); + + mState->mReply = QSharedPointer::create(); + + mAuthContext->setSslSession(tcTokenSessionTicket); + QSslConfiguration sslConfig; + sslConfig.setSessionTicket(resumptionSessionTicket); + + Env::getSingleton()->setDeveloperMode(developerMode); + + QCOMPARE(mState->checkAndSaveSessionResumption(sslConfig), failureCode); + } + + }; QTEST_GUILESS_MAIN(test_StateGenericSendReceive) diff -Nru ausweisapp2-2.3.1/test/qt/workflows/states/test_StateGetTcToken.cpp ausweisapp2-2.4.0/test/qt/workflows/states/test_StateGetTcToken.cpp --- ausweisapp2-2.3.1/test/qt/workflows/states/test_StateGetTcToken.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/states/test_StateGetTcToken.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -4,6 +4,8 @@ #include "states/StateGetTcToken.h" +#include "HttpServer.h" +#include "HttpServerRequestor.h" #include "ResourceLoader.h" #include "MockNetworkReply.h" @@ -20,10 +22,18 @@ { Q_OBJECT + private: + QUrl getUrl(const QString& pUrl) + { + const auto& port = QString::number(Env::getShared()->getServerPort()); + return QUrl(QStringLiteral("http://localhost:%1%2").arg(port, pUrl)); + } + private Q_SLOTS: void initTestCase() { ResourceLoader::getInstance().init(); + HttpServer::cPort = 0; } @@ -78,6 +88,88 @@ QTRY_COMPARE(spyAbort.count(), 1); // clazy:exclude=qstring-allocations QCOMPARE(context->getFailureCode(), FailureCode::Reason::Get_TcToken_Network_Error); } + + + void test_HttpError_FailureCode_data() + { + QTest::addColumn("httpStatusCode"); + QTest::addColumn("failureCode"); + + QTest::newRow("see other") << HTTP_STATUS_SEE_OTHER << FailureCode(FailureCode::Reason::Get_TcToken_Invalid_Redirect_Url); + QTest::newRow("found") << HTTP_STATUS_FOUND << FailureCode(FailureCode::Reason::Get_TcToken_Invalid_Redirect_Url); + QTest::newRow("temp redirect") << HTTP_STATUS_TEMPORARY_REDIRECT << FailureCode(FailureCode::Reason::Get_TcToken_Invalid_Redirect_Url); + + QTest::newRow("perm redirect") << HTTP_STATUS_PERMANENT_REDIRECT << FailureCode(FailureCode::Reason::Get_TcToken_Invalid_Server_Reply); + QTest::newRow("no content") << HTTP_STATUS_NO_CONTENT << FailureCode(FailureCode::Reason::Get_TcToken_Invalid_Server_Reply); + + QTest::newRow("bad request") << HTTP_STATUS_BAD_REQUEST << FailureCode(FailureCode::Reason::Get_TcToken_Client_Error); + QTest::newRow("not found") << HTTP_STATUS_NOT_FOUND << FailureCode(FailureCode::Reason::Get_TcToken_Client_Error); + + QTest::newRow("service unavailable") << HTTP_STATUS_SERVICE_UNAVAILABLE << FailureCode(FailureCode::Reason::Get_TcToken_ServiceUnavailable); + QTest::newRow("gateway timeout") << HTTP_STATUS_GATEWAY_TIMEOUT << FailureCode(FailureCode::Reason::Get_TcToken_Server_Error); + QTest::newRow("bad gateway") << HTTP_STATUS_BAD_GATEWAY << FailureCode(FailureCode::Reason::Get_TcToken_Server_Error); + QTest::newRow("server error") << HTTP_STATUS_INTERNAL_SERVER_ERROR << FailureCode(FailureCode::Reason::Get_TcToken_Server_Error); + } + + + void test_HttpError_FailureCode() + { + QFETCH(http_status, httpStatusCode); + QFETCH(FailureCode, failureCode); + + const auto& server = Env::getShared(); + QVERIFY(server->isListening()); + connect(server.data(), &HttpServer::fireNewHttpRequest, this, [httpStatusCode](const QSharedPointer& pRequest){ + pRequest->send(HttpResponse(httpStatusCode)); + }); + + HttpServerRequestor requestor; + const auto& reply = requestor.getRequest(getUrl(QStringLiteral("/dummy"))); + QVERIFY(reply->isFinished()); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), httpStatusCode); + + const QSharedPointer context(new AuthContext()); + StateGetTcToken state(context); + QSignalSpy spyAbort(&state, &StateGetTcToken::fireAbort); + + state.mReply = reply; + state.onNetworkReply(); + QTRY_COMPARE(spyAbort.count(), 1); // clazy:exclude=qstring-allocations + QCOMPARE(context->getFailureCode().value(), failureCode); + } + + + void test_Redirect_data() + { + QTest::addColumn("target"); + QTest::addColumn("redirect"); + + QTest::newRow("relative") << "index.html"_L1 << "https://a.not.existing.valid.test.url.com/index.html"_L1; + QTest::newRow("absolute") << "https://another.not.existing.valid.test.url.com/index.html"_L1 << "https://another.not.existing.valid.test.url.com/index.html"_L1; + } + + + void test_Redirect() + { + QFETCH(QLatin1String, target); + QFETCH(QLatin1String, redirect); + + const QSharedPointer context(new AuthContext()); + StateGetTcToken state(context); + QSignalSpy spyAbort(&state, &StateGetTcToken::fireAbort); + + auto reply = QSharedPointer::create(); + reply->setRequest(QNetworkRequest(QUrl("https://a.not.existing.valid.test.url.com/tcToken"_L1))); + reply->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, QVariant(302)); + reply->setAttribute(QNetworkRequest::RedirectionTargetAttribute, QVariant(target)); + state.mReply = reply; + + QTest::ignoreMessage(QtDebugMsg, "Status Code: 302 \"Found\""); + state.onNetworkReply(); + QCOMPARE(state.mReply->request().url(), QUrl(redirect)); + QTRY_COMPARE(spyAbort.count(), 1); // clazy:exclude=qstring-allocations + QCOMPARE(context->getFailureCode(), FailureCode::Reason::Get_TcToken_Network_Error); + } void test_ParseTcTokenNoData() diff -Nru ausweisapp2-2.3.1/test/qt/workflows/states/test_StateMaintainCardConnection.cpp ausweisapp2-2.4.0/test/qt/workflows/states/test_StateMaintainCardConnection.cpp --- ausweisapp2-2.3.1/test/qt/workflows/states/test_StateMaintainCardConnection.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/states/test_StateMaintainCardConnection.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -116,7 +116,7 @@ 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; + QTest::newRow("unexpected_transmit_status") << CardReturnCode::UNEXPECTED_TRANSMIT_STATUS << false; } diff -Nru ausweisapp2-2.3.1/test/qt/workflows/states/test_StatePreVerification.cpp ausweisapp2-2.4.0/test/qt/workflows/states/test_StatePreVerification.cpp --- ausweisapp2-2.3.1/test/qt/workflows/states/test_StatePreVerification.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/states/test_StatePreVerification.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -158,15 +158,9 @@ { const auto& remove = [](QList>& pVector, const QSharedPointer& pCert) { - QMutableListIterator> iter(pVector); - while (iter.hasNext()) - { - iter.next(); - if (*iter.value() == *pCert) - { - iter.remove(); - } - } + erase_if(pVector, [&pCert](const auto& cert){ + return *cert == *pCert; + }); }; auto& settings = Env::getSingleton()->getPreVerificationSettings(); diff -Nru ausweisapp2-2.3.1/test/qt/workflows/states/test_StateRedirectBrowser.cpp ausweisapp2-2.4.0/test/qt/workflows/states/test_StateRedirectBrowser.cpp --- ausweisapp2-2.3.1/test/qt/workflows/states/test_StateRedirectBrowser.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/states/test_StateRedirectBrowser.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -24,7 +24,6 @@ { const auto& context = QSharedPointer::create(true, QUrl(), pHandler); QSharedPointer state(StateBuilder::createState(context)); - state->onEntry(nullptr); return state; } @@ -110,13 +109,14 @@ auto state = getStateRedirectBrowser(); QSignalSpy spyAbort(state.data(), &StateRedirectBrowser::fireAbort); QSignalSpy spyContinue(state.data(), &StateRedirectBrowser::fireContinue); - auto context = state->getContext(); context->setRefreshUrl(QUrl("https://www.foo.bar"_L1)); context->setStatus(status); context->setStartPaosResult(result); - state->run(); + + state->onEntry(nullptr); QCOMPARE(context->getRefreshUrl(), url); + state->run(); QCOMPARE(spyAbort.count(), 0); QCOMPARE(spyContinue.count(), 1); } @@ -128,6 +128,7 @@ QSignalSpy spyAbort(state.data(), &StateRedirectBrowser::fireAbort); QSignalSpy spyContinue(state.data(), &StateRedirectBrowser::fireContinue); + state->onEntry(nullptr); state->run(); QCOMPARE(state->getContext()->getStatus(), GlobalStatus::Code::No_Error); QCOMPARE(spyAbort.count(), 0); @@ -145,6 +146,7 @@ QSignalSpy spyContinue(state.data(), &StateRedirectBrowser::fireContinue); QTest::ignoreMessage(QtCriticalMsg, "Cannot send page to caller: \"failing\""); + state->onEntry(nullptr); state->run(); QCOMPARE(state->getContext()->getStatus().getStatusCode(), GlobalStatus::Code::Workflow_Browser_Transmission_Error); QCOMPARE(state->getContext()->getStatus().getExternalInfo(), QLatin1String("failing")); @@ -175,8 +177,9 @@ QTest::ignoreMessage(QtDebugMsg, "Refresh URL is not valid: QUrl(\"\")"); QTest::ignoreMessage(QtDebugMsg, "CommunicationErrorAddress is not valid: QUrl(\"\")"); + state->onEntry(nullptr); state->run(); - QCOMPARE(context->getRefreshUrl(), QString()); + QCOMPARE(context->getRefreshUrl(), QUrl()); QCOMPARE(spyAbort.count(), 0); QCOMPARE(spyContinue.count(), 1); } @@ -206,8 +209,9 @@ context->setTcToken(QSharedPointer::create(tokenData)); QTest::ignoreMessage(QtDebugMsg, "Refresh URL is not valid: QUrl(\"\")"); + state->onEntry(nullptr); state->run(); - QCOMPARE(context->getRefreshUrl(), QStringLiteral("https://flupp?ResultMajor=error&ResultMinor=communicationError")); + QCOMPARE(context->getRefreshUrl(), QUrl(QStringLiteral("https://flupp?ResultMajor=error&ResultMinor=communicationError"))); QCOMPARE(spyAbort.count(), 0); QCOMPARE(spyContinue.count(), 1); } @@ -238,8 +242,9 @@ context->setTcToken(QSharedPointer::create(tokenData)); QTest::ignoreMessage(QtDebugMsg, "Refresh URL is not valid: QUrl(\"\")"); + state->onEntry(nullptr); state->run(); - QCOMPARE(context->getRefreshUrl(), QStringLiteral("https://flupp?ResultMajor=error&ResultMinor=communicationError")); + QCOMPARE(context->getRefreshUrl(), QUrl(QStringLiteral("https://flupp?ResultMajor=error&ResultMinor=communicationError"))); QCOMPARE(spyAbort.count(), 0); QCOMPARE(spyContinue.count(), 1); } diff -Nru ausweisapp2-2.3.1/test/qt/workflows/states/test_TermsOfUsage.cpp ausweisapp2-2.4.0/test/qt/workflows/states/test_TermsOfUsage.cpp --- ausweisapp2-2.3.1/test/qt/workflows/states/test_TermsOfUsage.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/states/test_TermsOfUsage.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -20,12 +20,12 @@ { QTest::addColumn("purpose"); - QTest::newRow("0") << QStringLiteral("- Anmeldung und Registrierung im Portal der Bundesagentur f\u00FCr Arbeit (BA) f\u00FCr Gesch\u00E4ftsvorf\u00E4lle, die im Portal der BA verf\u00FCgbar sind -"); - QTest::newRow("1") << QStringLiteral("- Authentisierung der Studierenden beim Login an Hochschuldiensten -"); + QTest::newRow("0") << QStringLiteral("- Anmeldung und Registrierung im Portal der Bundesagentur f\u00FCr Arbeit (BA) f\u00FCr Gesch\u00E4ftsvorf\u00E4lle, die im Portal der BA verf\u00FCgbar sind -"); // codespell:ignore + QTest::newRow("1") << QStringLiteral("- Authentisierung der Studierenden beim Login an Hochschuldiensten -"); // codespell:ignore QTest::newRow("2") << QStringLiteral("Registrierung / Login f\u00FCr \"Meine TK\""); QTest::newRow("3") << QStringLiteral("Permanentes B\u00FCrgerkonto"); QTest::newRow("4") << QStringLiteral("- Selbstauskunft -"); - QTest::newRow("5") << QStringLiteral("- Registrierung und Login f\u00FCr ein Benutzerkonto im HamburgService -"); + QTest::newRow("5") << QStringLiteral("- Registrierung und Login f\u00FCr ein Benutzerkonto im HamburgService -"); // codespell:ignore QTest::newRow("6") << QStringLiteral("- Altersverifikation f\u00FCr Online-Shop- und eCommerce-Anbieter per Web-Schnittstelle -"); QTest::newRow("7") << QStringLiteral("- Abwicklung von Verwaltungsleistungen mit Identifikationsbedarf ohne Registrierung -"); QTest::newRow("8") << QStringLiteral("Abwicklung von Verwaltungsleistungen ohne Registrierung"); @@ -37,7 +37,7 @@ QTest::newRow("14") << QStringLiteral("Registrierung / Login f\u00FCr \"meinCosmosDirekt\""); QTest::newRow("15") << QStringLiteral("- Selbstauskunft -"); QTest::newRow("16") << QStringLiteral("- Login B\u00FCrgerkonto \"Mein Hagen\" -"); - QTest::newRow("17") << QStringLiteral("- Verifikation von Personendaten zur Alters- und Identit\u00E4tsfeststellung -"); + QTest::newRow("17") << QStringLiteral("- Verifikation von Personendaten zur Alters- und Identit\u00E4tsfeststellung -"); // codespell:ignore QTest::newRow("18") << QStringLiteral("- Registrierung f\u00FCr die Virtuelle Poststelle bei der Deutschen Emissionshandelsstelle -"); } diff -Nru ausweisapp2-2.3.1/test/qt/workflows/test_TcToken.cpp ausweisapp2-2.4.0/test/qt/workflows/test_TcToken.cpp --- ausweisapp2-2.3.1/test/qt/workflows/test_TcToken.cpp 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/qt/workflows/test_TcToken.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -261,24 +261,11 @@ " " "") << false; - QTest::newRow("invalidServerAdress") << QByteArray("" - "" - " eid-server.example.de/entrypoint" - " 1A2BB129" - " https://service.example.de/loggedin?7eb39f62" - " https://service.example.de/ComError?7eb39f62" - " urn:liberty:paos:2006-08 " - " urn:ietf:rfc:4279 " - " " - " 4BC1A0B5 " - " " - "") << false; - - QTest::newRow("invalidRefreshAdress") << QByteArray("" + QTest::newRow("invalidServerAddress") << QByteArray("" "" - " https://eid-server.example.de/entrypoint" + " eid-server.example.de/entrypoint" " 1A2BB129" - " service.example.de/loggedin?7eb39f62" + " https://service.example.de/loggedin?7eb39f62" " https://service.example.de/ComError?7eb39f62" " urn:liberty:paos:2006-08 " " urn:ietf:rfc:4279 " @@ -287,18 +274,31 @@ " " "") << false; - QTest::newRow("invalidRefreshAdress") << QByteArray("" - "" - " https://eid-server.example.de/entrypoint" - " 1A2BB129" - " https://service.example.de/loggedin?7eb39f62" - " https://service.example.de/ComError?7eb39f62" - " urn:liberty:paos:2006-08 " - " urn:ietf:rfc:4279 " - " " - " 4BC1A0B56 " - " " - "") << false; + QTest::newRow("invalidRefreshAddress") << QByteArray("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " service.example.de/loggedin?7eb39f62" + " https://service.example.de/ComError?7eb39f62" + " urn:liberty:paos:2006-08 " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B5 " + " " + "") << false; + + QTest::newRow("invalidRefreshAddress") << QByteArray("" + "" + " https://eid-server.example.de/entrypoint" + " 1A2BB129" + " https://service.example.de/loggedin?7eb39f62" + " https://service.example.de/ComError?7eb39f62" + " urn:liberty:paos:2006-08 " + " urn:ietf:rfc:4279 " + " " + " 4BC1A0B56 " + " " + "") << false; QTest::newRow("valid") << QByteArray("" "" @@ -331,9 +331,9 @@ QTest::addColumn("pathProtocol"); QTest::addColumn("psk"); QTest::addColumn("identifier"); - QTest::addColumn("serverAdress"); - QTest::addColumn("errorAdress"); - QTest::addColumn("refreshAdress"); + QTest::addColumn("serverAddress"); + QTest::addColumn("errorAddress"); + QTest::addColumn("refreshAddress"); QTest::addColumn("messageType"); QTest::addColumn("valuesAreSchemaConform"); @@ -342,58 +342,58 @@ const QString pathProtocol = QStringLiteral("urn:ietf:rfc:4279"); const QByteArray psk("4BC1A0B5"); const QByteArray identifier("1A2BB129"); - const QString serverAdress = QStringLiteral("https://eid-server.example.de/entrypoint"); - const QString errorAdress = QStringLiteral("https://service.example.de/ComError?7eb39f62"); - const QString refreshAdress = QStringLiteral("https://service.example.de/loggedin?7eb39f62"); + const QString serverAddress = QStringLiteral("https://eid-server.example.de/entrypoint"); + const QString errorAddress = QStringLiteral("https://service.example.de/ComError?7eb39f62"); + const QString refreshAddress = QStringLiteral("https://service.example.de/loggedin?7eb39f62"); QTest::newRow("Binding is no valid anyUri: \"\"") << QString() << pathProtocol << psk << identifier - << serverAdress << errorAdress << refreshAdress + << serverAddress << errorAddress << refreshAddress << QtCriticalMsg << false; QTest::newRow("Binding is no valid anyUri: \"://://\"") << u"://://"_s << pathProtocol << psk << identifier - << serverAdress << errorAdress << refreshAdress + << serverAddress << errorAddress << refreshAddress << QtCriticalMsg << false; QTest::newRow("PathSecurity-Protocol is no valid URI: \"\"") << binding << u""_s << psk << identifier - << serverAdress << errorAdress << refreshAdress + << serverAddress << errorAddress << refreshAddress << QtCriticalMsg << true; QTest::newRow("PSK is null") << binding << pathProtocol << QByteArray() << identifier - << serverAdress << errorAdress << refreshAdress + << serverAddress << errorAddress << refreshAddress << QtWarningMsg << true; QTest::newRow("SessionIdentifier is null") << binding << pathProtocol << psk << QByteArray() - << serverAdress << errorAdress << refreshAdress + << serverAddress << errorAddress << refreshAddress << QtWarningMsg << true; QTest::newRow("ServerAddress no valid anyUri: \"\"") << binding << pathProtocol << psk << identifier - << QString() << errorAdress << refreshAdress + << QString() << errorAddress << refreshAddress << QtCriticalMsg << false; QTest::newRow("ServerAddress no valid anyUri: \"://://\"") << binding << pathProtocol << psk << identifier - << u"://://"_s << errorAdress << refreshAdress + << u"://://"_s << errorAddress << refreshAddress << QtCriticalMsg << false; QTest::newRow("CommunicationErrorAddress no valid anyUri: \"://://\"") << binding << pathProtocol << psk << identifier - << serverAdress << u"://://"_s << refreshAdress + << serverAddress << u"://://"_s << refreshAddress << QtCriticalMsg << false; QTest::newRow("RefreshAddress no valid anyUri: \"\"") << binding << pathProtocol << psk << identifier - << serverAdress << errorAdress << QString() + << serverAddress << errorAddress << QString() << QtCriticalMsg << false; QTest::newRow("RefreshAddress no valid anyUri: \"://://\"") << binding << pathProtocol << psk << identifier - << serverAdress << errorAdress << u"://://"_s + << serverAddress << errorAddress << u"://://"_s << QtCriticalMsg << false; } @@ -404,9 +404,9 @@ QFETCH(QString, pathProtocol); QFETCH(QByteArray, psk); QFETCH(QByteArray, identifier); - QFETCH(QString, serverAdress); - QFETCH(QString, errorAdress); - QFETCH(QString, refreshAdress); + QFETCH(QString, serverAddress); + QFETCH(QString, errorAddress); + QFETCH(QString, refreshAddress); QFETCH(QtMsgType, messageType); QFETCH(bool, valuesAreSchemaConform); @@ -425,7 +425,7 @@ "")); QTest::ignoreMessage(messageType, QTest::currentDataTag()); - QCOMPARE(token.valuesAreSchemaConform(binding, pathProtocol, psk, identifier, serverAdress, errorAdress, refreshAdress), valuesAreSchemaConform); + QCOMPARE(token.valuesAreSchemaConform(binding, pathProtocol, psk, identifier, serverAddress, errorAddress, refreshAddress), valuesAreSchemaConform); } diff -Nru ausweisapp2-2.3.1/test/tools/CMakeLists.txt ausweisapp2-2.4.0/test/tools/CMakeLists.txt --- ausweisapp2-2.3.1/test/tools/CMakeLists.txt 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/tools/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -22,7 +22,7 @@ endif() -if(TARGET ${Qt}::Qml AND QT_VERSION VERSION_GREATER_EQUAL 6.5) +if(TARGET ${Qt}::Qml) add_test(NAME qmldir COMMAND ${CMAKE_COMMAND} -DCMD=CHECK_QMLDIR -P ${CMAKE_DIR}/cmd.cmake WORKING_DIRECTORY $) @@ -33,6 +33,11 @@ WORKING_DIRECTORY $) set_tests_properties(qmltypes PROPERTIES LABELS "qml") + add_test(NAME qmlenums + COMMAND ${CMAKE_COMMAND} -DCMD=CHECK_QMLENUMS -P ${CMAKE_DIR}/cmd.cmake + WORKING_DIRECTORY $) + set_tests_properties(qmltypes PROPERTIES LABELS "qml") + if(Python_FOUND) add_test(NAME qmlresources COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/checkQmlResources.py @@ -43,8 +48,48 @@ set_tests_properties(qmlresources PROPERTIES LABELS "qml") endif() - add_test(NAME qmllint - COMMAND ${CMAKE_COMMAND} --build . --target all_qmllint - WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) - set_tests_properties(qmllint PROPERTIES LABELS "qml") + add_subdirectory(qmllint) +endif() + + +find_program(TYPOS_BIN typos) +if(TYPOS_BIN) + add_test(NAME typos + COMMAND ${TYPOS_BIN} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + set_tests_properties(typos PROPERTIES LABELS "sanity") +endif() + +find_program(CODESPELL_BIN codespell) +if(CODESPELL_BIN) + add_test(NAME codespell + COMMAND ${CODESPELL_BIN} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + set_tests_properties(codespell PROPERTIES LABELS "sanity") +endif() + +find_program(SPHINX_LINT_BIN sphinx-lint) +if(SPHINX_LINT_BIN) + add_test(NAME sphinx-lint + COMMAND ${SPHINX_LINT_BIN} -e all --max-line-length 120 -i LC_MESSAGES + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + set_tests_properties(codespell PROPERTIES LABELS "sanity") +endif() + +find_program(YAMLLINT_BIN yamllint) +if(YAMLLINT_BIN) + add_test(NAME yamllint + COMMAND ${YAMLLINT_BIN} --strict ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + set_tests_properties(yamllint PROPERTIES LABELS "sanity") +endif() + +if(NOT RUFF) # See Tools.cmake + find_program(RUFF ruff) +endif() +if(RUFF) + add_test(NAME ruff + COMMAND ${RUFF} check ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + set_tests_properties(ruff PROPERTIES LABELS "sanity") endif() diff -Nru ausweisapp2-2.3.1/test/tools/checkQmlResources.py ausweisapp2-2.4.0/test/tools/checkQmlResources.py --- ausweisapp2-2.3.1/test/tools/checkQmlResources.py 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/test/tools/checkQmlResources.py 2025-10-30 10:10:48.000000000 +0000 @@ -8,15 +8,21 @@ class CommandLineParser(argparse.ArgumentParser): def __init__(self): super().__init__() - self.add_argument("--module-dir", help="Directory of QML modules", - required=True) - self.add_argument("--resources-dir", help="Directory of QML resources", - required=True) - self.add_argument("--exclude-filenames", - help="Filenames to exclude from QML module check") - self.add_argument("--exclude-resources", - help="Path to textfile that defines excludes for QML" - "resource usage check") + self.add_argument( + '--module-dir', help='Directory of QML modules', required=True + ) + self.add_argument( + '--resources-dir', help='Directory of QML resources', required=True + ) + self.add_argument( + '--exclude-filenames', + help='Filenames to exclude from QML module check', + ) + self.add_argument( + '--exclude-resources', + help='Path to textfile that defines excludes for QML' + 'resource usage check', + ) self.args = self.parse_args() def qml_module_dir(self): @@ -28,7 +34,7 @@ def exclude_filenames(self): if self.args.exclude_filenames is None: return [] - return self.args.exclude_filenames.split(",") + return self.args.exclude_filenames.split(',') def exclude_resources(self): return self.args.exclude_resources @@ -37,15 +43,20 @@ class QmlFileCache: def __init__(self, qml_dir): self.cache = dict() + self.qml_dir = qml_dir + if not self.qml_dir.endswith('/'): + self.qml_dir += '/' + for path in self.collect_qml_files(qml_dir): - with open(path, "r", encoding="utf8") as fp: - self.cache[path] = [line.strip() for line - in fp.read().splitlines()] + with open(path, 'r', encoding='utf8') as fp: + self.cache[path] = [ + line.strip() for line in fp.read().splitlines() + ] def collect_qml_files(self, qml_dir): for root, _, files in os.walk(qml_dir): for file in files: - if os.path.splitext(file)[-1].lower() != ".qml": + if os.path.splitext(file)[-1].lower() != '.qml': continue path = os.path.join(root, file) @@ -55,7 +66,7 @@ return len(self.cache.keys()) == 0 def in_cache(self, search_string): - pattern = re.compile(r"\b%s" % search_string) + pattern = re.compile(r'\b%s' % search_string) for lines in self.cache.values(): for line in lines: if re.search(pattern, line): @@ -68,51 +79,53 @@ return False lines = self.cache[filename] - return "pragma Singleton" in lines + return 'pragma Singleton' in lines def unused_qml_files(self, exclude_filenames): - found_unused_file = False + def strip_basepath(path): + return file.split(self.qml_dir)[-1] + + unused_files = [] for file in self.cache.keys(): filename, ext = os.path.splitext(os.path.split(file)[-1]) if filename in exclude_filenames: - print("Skipping", file) + print('Skipping', file) continue if self.is_singleton(file): - if not self.in_cache(filename + "."): - print("QML singleton unused", file) - found_unused_file = True - elif not self.in_cache(filename + " {"): - print("QML file unused", file) - found_unused_file = True + if not self.in_cache(filename + '.'): + print('QML singleton unused', strip_basepath(file)) + unused_files.append(strip_basepath(file)) + elif not self.in_cache(filename + ' {'): + print('QML file unused', strip_basepath(file)) + unused_files.append(strip_basepath(file)) - return found_unused_file + return unused_files class ResourceCache: def __init__(self, resources_dir): - self.language_names = ["de", "en", "uk", "ru"] + self.language_names = ['de', 'en', 'uk', 'ru'] self.qrc_content = set() - self.found_duplicate = False + self.duplicate_files = [] for qrc_file in self.collect_qrc_files(resources_dir): - with open(qrc_file, "r", encoding="utf8") as fp: + with open(qrc_file, 'r', encoding='utf8') as fp: for line in [line.strip() for line in fp.read().splitlines()]: - m = re.match("(.+)", line) + m = re.match('(.+)', line) if m is None: continue if m.group(1) in self.qrc_content: - self.found_duplicate = True - print("Duplicate QML resource in", qrc_file, - m.group(1)) + self.duplicate_files.append(m.group(1)) + print('Duplicate:', qrc_file, m.group(1)) self.qrc_content.add(m.group(1)) def collect_qrc_files(self, resources_dir): for root, _, files in os.walk(resources_dir): for file in files: - if os.path.splitext(file)[-1].lower() != ".qrc": + if os.path.splitext(file)[-1].lower() != '.qrc': continue path = os.path.join(root, file) @@ -122,34 +135,34 @@ return len(self.qrc_content) == 0 def resource_file_missing(self, resources_dir): - file_missing = False + missing_files = [] for resource in self.qrc_content: if not os.path.exists(os.path.join(resources_dir, resource)): - print("QML resource is missing", resource) - file_missing = True + print('Missing:', resource) + missing_files.append(resource) - return file_missing - - def has_duplicate(self): - return self.found_duplicate + return missing_files def resource_unused(self, qml_cache, excluded_resources): - resource_unused = False + unused_resources = [] for line in sorted(self.qrc_content): if excluded_resources.is_excluded(line): - print("QML resource is excluded", line) + print('Skipping:', line) + continue + + if qml_cache.in_cache(line): continue if self.is_language_aware_resource(line): language_filename = self.language_aware_filename(line) if not qml_cache.in_cache(language_filename): - print("Language aware QML resource is unused", line) - resource_unused = True - elif not qml_cache.in_cache(line): - print("QML resource is unused", line) - resource_unused = True + print('UNUSED language aware files:', line) + unused_resources.append(line) + else: + print('UNUSED:', line) + unused_resources.append(line) - return resource_unused + return unused_resources def is_language_aware_resource(self, resource_file): return self.language_aware_filename(resource_file) is not None @@ -157,9 +170,9 @@ def language_aware_filename(self, resource_file): filename, ext = os.path.splitext(resource_file) for language_name in self.language_names: - pattern = re.compile("_" + language_name + "$") + pattern = re.compile('_' + language_name + '$') if re.search(pattern, filename): - return re.sub(pattern, "_%1", filename) + ext + return re.sub(pattern, '_%1', filename) + ext return None @@ -167,35 +180,66 @@ class ExcludedResources: def __init__(self, excludes_filename): if excludes_filename: - with open(excludes_filename, "r", encoding="utf8") as fp: - self.excluded_resources = [re.compile(line.strip()) for line - in fp.readlines()] + with open(excludes_filename, 'r', encoding='utf8') as fp: + self.excluded_resources = [ + re.compile(line.strip()) for line in fp.readlines() + ] else: self.excluded_resources = [] def is_excluded(self, filename): - return any([expression.fullmatch(filename) for expression - in self.excluded_resources]) + return any( + [ + expression.fullmatch(filename) + for expression in self.excluded_resources + ] + ) + +if __name__ == '__main__': + + def print_if_not_empty(list_obj, message): + if len(list_obj): + print('\n%s' % message) + for line in list_obj: + print(' ', line) -if __name__ == "__main__": parser = CommandLineParser() qml_cache = QmlFileCache(parser.qml_module_dir()) if qml_cache.is_empty(): - print("Found no QML files in", parser.qml_module_dir()) + print('Found no QML files in', parser.qml_module_dir()) exit(1) resource_cache = ResourceCache(parser.resources_dir()) if resource_cache.is_empty(): - print("Found no QML resources in", parser.resources_dir()) + print('Found no resources in', parser.resources_dir()) exit(1) excluded_resources = ExcludedResources(parser.exclude_resources()) - has_error = qml_cache.unused_qml_files(parser.exclude_filenames()) - has_error |= resource_cache.resource_file_missing(parser.resources_dir()) - has_error |= resource_cache.has_duplicate() - has_error |= resource_cache.resource_unused(qml_cache, excluded_resources) - - exit(has_error) + unused_qml_files = qml_cache.unused_qml_files(parser.exclude_filenames()) + missing_resource_files = resource_cache.resource_file_missing( + parser.resources_dir() + ) + duplicate_files = resource_cache.duplicate_files + unused_resources = resource_cache.resource_unused( + qml_cache, excluded_resources + ) + + if any( + [ + len(unused_qml_files), + len(missing_resource_files), + len(duplicate_files), + len(unused_resources), + ] + ): + print('\n') + print_if_not_empty(unused_qml_files, 'Unused QML file(s):') + print_if_not_empty(missing_resource_files, 'Resource file(s) missing:') + print_if_not_empty(duplicate_files, 'Duplicated resources:') + print_if_not_empty(unused_resources, 'Unused resources:') + exit(1) + else: + exit(0) diff -Nru ausweisapp2-2.3.1/test/tools/qmllint/CMakeLists.txt ausweisapp2-2.4.0/test/tools/qmllint/CMakeLists.txt --- ausweisapp2-2.3.1/test/tools/qmllint/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/tools/qmllint/CMakeLists.txt 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,14 @@ +if(QT_VERSION VERSION_GREATER_EQUAL 6.9) + if(TARGET ${Qt}::QmlCompiler) + qt_add_plugin(PluginQmllint) + file(GLOB_RECURSE FILES "*.cpp") + target_sources(PluginQmllint PRIVATE ${FILES}) + target_link_libraries(PluginQmllint PRIVATE Qt::QmlCompiler) + endif() + + get_filename_component(PARENT_DIR "${CMAKE_CURRENT_BINARY_DIR}" DIRECTORY) + add_test(NAME qmllint + COMMAND ${CMAKE_COMMAND} -E env QT_PLUGIN_PATH=${PARENT_DIR} -- ${CMAKE_COMMAND} --build . --target all_qmllint + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + set_tests_properties(qmllint PROPERTIES LABELS "qml") +endif() diff -Nru ausweisapp2-2.3.1/test/tools/qmllint/ElementPass.cpp ausweisapp2-2.4.0/test/tools/qmllint/ElementPass.cpp --- ausweisapp2-2.3.1/test/tools/qmllint/ElementPass.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/tools/qmllint/ElementPass.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#include "ElementPass.h" + +#include + +using namespace governikus; + +ElementPass::ElementPass(QQmlSA::PassManager* pManager) + : QQmlSA::ElementPass(pManager) +{ +} + + +void ElementPass::run(const QQmlSA::Element& pElement) +{ + const QRegularExpression regex(QStringLiteral(R"(source (===|!==) ["'])")); + + const auto& bindings = pElement.ownPropertyBindings(); + for (const auto& binding : bindings) + { + if (binding.bindingType() == QQmlSA::BindingType::Script && + regex.match(sourceCode(binding.sourceLocation())).hasMatch()) + { + emitWarning("Use source.toString() as it is an URL", LogIdSource, binding.sourceLocation()); + } + } +} diff -Nru ausweisapp2-2.3.1/test/tools/qmllint/ElementPass.h ausweisapp2-2.4.0/test/tools/qmllint/ElementPass.h --- ausweisapp2-2.3.1/test/tools/qmllint/ElementPass.h 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/tools/qmllint/ElementPass.h 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include + +namespace governikus +{ +static constexpr QQmlSA::LoggerWarningId LogIdSource {"Plugin.AusweisApp.source"}; + +class ElementPass + : public QQmlSA::ElementPass +{ + public: + explicit ElementPass(QQmlSA::PassManager* pManager); + void run(const QQmlSA::Element& pElement) override; +}; + +} // namespace governikus diff -Nru ausweisapp2-2.3.1/test/tools/qmllint/Plugin.cpp ausweisapp2-2.4.0/test/tools/qmllint/Plugin.cpp --- ausweisapp2-2.3.1/test/tools/qmllint/Plugin.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/tools/qmllint/Plugin.cpp 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#include "Plugin.h" + +#include "ElementPass.h" + +using namespace governikus; + +void Plugin::registerPasses(QQmlSA::PassManager* pManager, const QQmlSA::Element& pRootElement) +{ + Q_UNUSED(pRootElement) + + if (pManager->isCategoryEnabled(LogIdSource)) + { + pManager->registerElementPass(std::make_unique(pManager)); + } +} diff -Nru ausweisapp2-2.3.1/test/tools/qmllint/Plugin.h ausweisapp2-2.4.0/test/tools/qmllint/Plugin.h --- ausweisapp2-2.3.1/test/tools/qmllint/Plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/tools/qmllint/Plugin.h 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2025 Governikus GmbH & Co. KG, Germany + */ + +#pragma once + +#include +#include + +namespace governikus +{ +class Plugin + : public QObject + , public QQmlSA::LintPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QmlLintPluginInterface_iid FILE "metadata.json") + Q_INTERFACES(QQmlSA::LintPlugin) + + public: + void registerPasses(QQmlSA::PassManager* pManager, const QQmlSA::Element& pRootElement) override; +}; +} // namespace governikus diff -Nru ausweisapp2-2.3.1/test/tools/qmllint/metadata.json ausweisapp2-2.4.0/test/tools/qmllint/metadata.json --- ausweisapp2-2.3.1/test/tools/qmllint/metadata.json 1970-01-01 00:00:00.000000000 +0000 +++ ausweisapp2-2.4.0/test/tools/qmllint/metadata.json 2025-10-30 10:10:48.000000000 +0000 @@ -0,0 +1,13 @@ +{ + "name": "AusweisApp", + "author": "Governikus", + "description": "Special linting for AusweisApp", + "version": "1.0", + "loggingCategories": [ + { + "name": "source", + "settingsName": "source", + "description": "Check toString() of source property" + } + ] +} diff -Nru ausweisapp2-2.3.1/uncrustify.cfg ausweisapp2-2.4.0/uncrustify.cfg --- ausweisapp2-2.3.1/uncrustify.cfg 2025-03-17 11:12:40.000000000 +0000 +++ ausweisapp2-2.4.0/uncrustify.cfg 2025-10-30 10:10:48.000000000 +0000 @@ -1,4 +1,4 @@ -# Uncrustify_d-0.80.1_f +# Uncrustify-0.81.0_f # # General options @@ -345,7 +345,7 @@ sp_angle_colon = ignore # ignore/add/remove/force # Add or remove space after '>'. -sp_after_angle = remove # ignore/add/remove/force +sp_after_angle = remove # ignore/add/remove/force # Add or remove space between '>' and '(' as found in 'new List(foo);'. sp_angle_paren = remove # ignore/add/remove/force @@ -881,6 +881,7 @@ sp_addr = remove # ignore/add/remove/force # Add or remove space around the '.' or '->' operators. +# also the c-sharp null-conditional operator '?.' # # Default: remove sp_member = remove # ignore/add/remove/force @@ -1499,6 +1500,9 @@ # parentheses. indent_ignore_bool = false # true/false +# Whether to indent lines that are nested in boolean expression one more level for each nesting +indent_bool_nested_all = false # true/false + # Whether to ignore the indentation of an arithmetic operator. indent_ignore_arith = false # true/false @@ -2189,6 +2193,12 @@ # structures. If set to ignore, uses nl_after_brace_close. nl_brace_struct_var = ignore # ignore/add/remove/force +# Whether to add a newline before/after each '&&' or `||` on the same +# nesting level in a boolean expression if boolean expression will not +# fit on a line. code_width needs to be positive and pos_bool needs +# to be 'lead' or 'trail' for this option to have any effect. +nl_bool_expr_hierarchical = false # true/false + # Whether to alter newlines in '#define' macros. nl_define_macro = false # true/false @@ -2505,7 +2515,7 @@ nl_after_access_spec = 1 # unsigned number # The number of newlines between a function definition and the function -# comment, as in '// comment\n void foo() {...}'. +# comment, as in '/* comment */ void foo() {...}'. # # 0: No change (default). nl_comment_func_def = 1 # unsigned number