diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index f2872cf47..4f8d4d245 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/ios/Podfile b/ios/Podfile index 015ad2565..867d660ff 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -38,7 +38,42 @@ post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) target.build_configurations.each do |config| - config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' + config.build_settings['ENABLE_BITCODE'] = 'NO' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' + # You can remove unused permissions here + # for more infomation: https://github.com/BaseflowIT/flutter-permission-handler/blob/master/permission_handler/ios/Classes/PermissionHandlerEnums.h + # e.g. when you don't need camera permission, just add 'PERMISSION_CAMERA=0' + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ + '$(inherited)', + ## dart: PermissionGroup.calendar + 'PERMISSION_EVENTS=0', + ## dart: PermissionGroup.reminders + 'PERMISSION_REMINDERS=0', + ## dart: PermissionGroup.contacts + 'PERMISSION_CONTACTS=0', + ## dart: PermissionGroup.camera + 'PERMISSION_CAMERA=1', + ## dart: PermissionGroup.microphone + 'PERMISSION_MICROPHONE=0', + ## dart: PermissionGroup.speech + 'PERMISSION_SPEECH_RECOGNIZER=0', + ## dart: PermissionGroup.photos + 'PERMISSION_PHOTOS=0', + ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse] + 'PERMISSION_LOCATION=0', + ## dart: PermissionGroup.notification + 'PERMISSION_NOTIFICATIONS=1', + ## dart: PermissionGroup.mediaLibrary + 'PERMISSION_MEDIA_LIBRARY=0', + ## dart: PermissionGroup.sensors + 'PERMISSION_SENSORS=0', + ## dart: PermissionGroup.bluetooth + 'PERMISSION_BLUETOOTH=0', + ## dart: PermissionGroup.appTrackingTransparency + 'PERMISSION_APP_TRACKING_TRANSPARENCY=0', + ## dart: PermissionGroup.criticalAlerts + 'PERMISSION_CRITICAL_ALERTS=0', + ] end end end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6cad51494..a03e82a17 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,12 +3,12 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 0DA36CBD26D4E8F400CDA64B /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0DA36CBC26D4E8F400CDA64B /* GoogleService-Info.plist */; }; - 0DCCE7B526CE7AA30029E1D5 /* BuildFile in Resources */ = {isa = PBXBuildFile; }; + 0DCCE7B526CE7AA30029E1D5 /* (null) in Resources */ = {isa = PBXBuildFile; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; @@ -173,13 +173,12 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - DevelopmentTeam = 627QALYL3B; - LastSwiftMigration = 0910; + LastSwiftMigration = ""; }; }; }; @@ -210,7 +209,7 @@ files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 0DCCE7B526CE7AA30029E1D5 /* BuildFile in Resources */, + 0DCCE7B526CE7AA30029E1D5 /* (null) in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 0DA36CBD26D4E8F400CDA64B /* GoogleService-Info.plist in Resources */, @@ -222,10 +221,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -236,6 +237,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -256,7 +258,7 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", - "${BUILT_PRODUCTS_DIR}/FirebaseCoreDiagnostics/FirebaseCoreDiagnostics.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", "${BUILT_PRODUCTS_DIR}/FirebaseMessaging/FirebaseMessaging.framework", "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", @@ -264,28 +266,29 @@ "${BUILT_PRODUCTS_DIR}/GoogleToolboxForMac/GoogleToolboxForMac.framework", "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", - "${BUILT_PRODUCTS_DIR}/Protobuf/Protobuf.framework", + "${BUILT_PRODUCTS_DIR}/ReachabilitySwift/Reachability.framework", "${BUILT_PRODUCTS_DIR}/SwiftyRSA/SwiftyRSA.framework", "${BUILT_PRODUCTS_DIR}/Toast/Toast.framework", - "${BUILT_PRODUCTS_DIR}/catcher/catcher.framework", + "${BUILT_PRODUCTS_DIR}/connectivity_plus/connectivity_plus.framework", "${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework", + "${BUILT_PRODUCTS_DIR}/flutter_local_notifications/flutter_local_notifications.framework", "${BUILT_PRODUCTS_DIR}/flutter_mailer/flutter_mailer.framework", "${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.framework", "${BUILT_PRODUCTS_DIR}/fluttertoast/fluttertoast.framework", - "${BUILT_PRODUCTS_DIR}/local_auth/local_auth.framework", + "${BUILT_PRODUCTS_DIR}/local_auth_ios/local_auth_ios.framework", "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", "${BUILT_PRODUCTS_DIR}/native_device_orientation/native_device_orientation.framework", - "${BUILT_PRODUCTS_DIR}/package_info/package_info.framework", "${BUILT_PRODUCTS_DIR}/package_info_plus/package_info_plus.framework", + "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", "${BUILT_PRODUCTS_DIR}/pi_authenticator_legacy/pi_authenticator_legacy.framework", - "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework", + "${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework", "${BUILT_PRODUCTS_DIR}/uni_links/uni_links.framework", - "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", + "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreDiagnostics.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseInstallations.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseMessaging.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", @@ -293,23 +296,24 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleToolboxForMac.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Protobuf.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyRSA.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Toast.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/catcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/connectivity_plus.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_local_notifications.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_mailer.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fluttertoast.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/native_device_orientation.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/package_info_plus.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/pi_authenticator_legacy.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/uni_links.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -418,7 +422,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -441,13 +445,18 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "privacyIDEA Authenticator"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 3.1.1; + MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; PRODUCT_BUNDLE_IDENTIFIER = privacyidea.authenticator; PRODUCT_NAME = "privacyIDEA Authenticator"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -504,7 +513,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -554,7 +563,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -578,13 +587,18 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "privacyIDEA Authenticator"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 3.1.1; + MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; PRODUCT_BUNDLE_IDENTIFIER = privacyidea.authenticator; PRODUCT_NAME = "privacyIDEA Authenticator"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -609,13 +623,18 @@ "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "privacyIDEA Authenticator"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 3.1.1; + MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; PRODUCT_BUNDLE_IDENTIFIER = privacyidea.authenticator; PRODUCT_NAME = "privacyIDEA Authenticator"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 8dc3b542c..b9671f2b0 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ - NSFaceIDUsageDescription - Use face id to prevent unauthorized access to tokens. - ITSAppUsesNonExemptEncryption - + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -27,6 +25,19 @@ $(FLUTTER_BUILD_NAME) CFBundleSignature ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + it.netknights.otpauth + CFBundleURLSchemes + + otpauth + + + CFBundleVersion $(FLUTTER_BUILD_NUMBER) FIREBASE_ANALYTICS_COLLECTION_DISABLED @@ -35,10 +46,18 @@ FirebaseScreenReportingEnabled + ITSAppUsesNonExemptEncryption + LSRequiresIPhoneOS NSCameraUsageDescription Camera permission is required for scanning QR codes. + NSFaceIDUsageDescription + Use face id to prevent unauthorized access to tokens. + NSLocalNetworkUsageDescription + Network access is required to roll out push tokens. + UIApplicationSupportsIndirectInputEvents + UIBackgroundModes fetch @@ -50,33 +69,18 @@ Main UISupportedInterfaceOrientations - UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait UISupportedInterfaceOrientations~ipad - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown UIViewControllerBasedStatusBarAppearance - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - it.netknights.otpauth - CFBundleURLSchemes - - otpauth - - - - NSLocalNetworkUsageDescription - Network access is required to roll out push tokens. diff --git a/lib/l10n/app_cs.arb b/lib/l10n/app_cs.arb index ac0758fd6..5b7613374 100644 --- a/lib/l10n/app_cs.arb +++ b/lib/l10n/app_cs.arb @@ -678,10 +678,16 @@ "description": "errorRollOutSSLHandshakeFailed", "type": "text" }, - "errorWhenPullingChallenges": "Při dotazování na výzvy došlo k chybě.", + "errorWhenPullingChallenges": "Při dotazování na výzvy {name} došlo k chybě.", "@errorWhenPullingChallenges": { "description": "errorWhenPullingChallenges", - "type": "text" + "type": "text", + "placeholders": { + "name": { + "type": "String", + "example": "PUSH1234A" + } + } }, "errorRollOutTokenExpired": "Roll-out tohoto tokenu již není možný.\nPlatnost tokenu {name} vypršela.", "@errorRollOutTokenExpired": { diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 5585fe688..a7b5350cf 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -648,10 +648,16 @@ "description": "imageUrl", "type": "text" }, - "errorWhenPullingChallenges": "Fehler beim Abrufen der Authentifizierungsanfragen.", + "errorWhenPullingChallenges": "Fehler beim Abrufen der Authentifizierungsanfragen von {name}", "@errorWhenPullingChallenges": { "description": "errorWhenPullingChallenges", - "type": "text" + "type": "text", + "placeholders": { + "name": { + "type": "String", + "example": "PUSH1234A" + } + } }, "errorRollOutNoConnectionToServer": "Der Rollout von Token {name} ist fehlgeschlagen, der Server konnte nicht erreicht werden.", "@errorRollOutNoConnectionToServer": { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 668fa25b7..8624668e6 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -663,10 +663,16 @@ "description": "Tells the user that the roll-out failed because the SSL handshake failed.", "type": "text" }, - "errorWhenPullingChallenges": "An error occured when polling for challenges", + "errorWhenPullingChallenges": "An error occured when polling for challenges of {name}", "@errorWhenPullingChallenges": { "description": "errorWhenPullingChallenges", - "type": "text" + "type": "text", + "placeholders": { + "name": { + "type": "String", + "example": "PUSH1234A" + } + } }, "errorRollOutTokenExpired": "Rolling out this Token is not possible anymore.\nThe token {name} has expired.", "@errorRollOutTokenExpired": { diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 69e67bb53..4cd3fb4f0 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -678,10 +678,16 @@ "description": "errorRollOutSSLHandshakeFailed", "type": "text" }, - "errorWhenPullingChallenges": "Se ha producido un error al buscar retos.", + "errorWhenPullingChallenges": "Se ha producido un error al buscar retos de {name}", "@errorWhenPullingChallenges": { "description": "errorWhenPullingChallenges", - "type": "text" + "type": "text", + "placeholders": { + "name": { + "type": "String", + "example": "PUSH1234A" + } + } }, "errorRollOutTokenExpired": "El despliegue de este token ya no es posible.\nEl token {name} ha caducado.", "@errorRollOutTokenExpired": { diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 3b4629ed7..0e4ad4b9f 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -673,10 +673,16 @@ "description": "errorRollOutSSLHandshakeFailed", "type": "text" }, - "errorWhenPullingChallenges": "Une erreur s'est produite lors de l'interrogation des défis", + "errorWhenPullingChallenges": "Une erreur s'est produite lors de l'interrogation des défis de {name}", "@errorWhenPullingChallenges": { "description": "errorWhenPullingChallenges", - "type": "text" + "type": "text", + "placeholders": { + "name": { + "type": "String", + "example": "PUSH1234A" + } + } }, "errorRollOutTokenExpired": "Le déploiement de ce jeton n'est plus possible. Le jeton {name} a expiré.", "@errorRollOutTokenExpired": { diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index a0e82547f..589acc423 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -667,10 +667,16 @@ "description": "Message for the rollout process", "type": "text" }, - "errorWhenPullingChallenges": "Er is een fout opgetreden bij het peilen naar uitdagingen", + "errorWhenPullingChallenges": "Er is een fout opgetreden bij het zoeken naar uitdagingen van {name}", "@errorWhenPullingChallenges": { "description": "errorWhenPullingChallenges", - "type": "text" + "type": "text", + "placeholders": { + "name": { + "type": "String", + "example": "PUSH1234A" + } + } }, "errorRollOutTokenExpired": "Het uitrollen van dit token is niet meer mogelijk.\nHet token {name} is verlopen.", "@errorRollOutTokenExpired": { diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 8e7a8a62e..5765d27ca 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -667,10 +667,16 @@ "description": "errorRollOutSSLHandshakeFailed", "type": "text" }, - "errorWhenPullingChallenges": "An error occured when polling for challenges", + "errorWhenPullingChallenges": "Wystąpił błąd podczas odpytywania o wyzwania {name}", "@errorWhenPullingChallenges": { "description": "errorWhenPullingChallenges", - "type": "text" + "type": "text", + "placeholders": { + "name": { + "type": "String", + "example": "PUSH1234A" + } + } }, "errorRollOutTokenExpired": "Wstać z łóżka tego tokena nie jest już możliwe.\nToken {name} wygasł.", "@errorRollOutTokenExpired": { diff --git a/lib/main.dart b/lib/main.dart index 90c057774..0c6f2ff68 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -109,7 +109,7 @@ class _SplashScreenState extends ConsumerState { @override void initState() { super.initState(); - + Logger.info('Starting app.', name: 'main.dart#initState'); Future.delayed(_splashScreenDelay, () { if (mounted) { setState(() { diff --git a/lib/model/enums/schemes.dart b/lib/model/enums/schemes.dart new file mode 100644 index 000000000..a55c9f1f4 --- /dev/null +++ b/lib/model/enums/schemes.dart @@ -0,0 +1,4 @@ +enum UriSchemes { + otpauth, + pia, +} diff --git a/lib/model/token_folder.g.dart b/lib/model/token_folder.g.dart index a3815e6db..a7ebd95c2 100644 --- a/lib/model/token_folder.g.dart +++ b/lib/model/token_folder.g.dart @@ -14,7 +14,8 @@ TokenFolder _$TokenFolderFromJson(Map json) => TokenFolder( sortIndex: json['sortIndex'] as int?, ); -Map _$TokenFolderToJson(TokenFolder instance) => { +Map _$TokenFolderToJson(TokenFolder instance) => + { 'label': instance.label, 'folderId': instance.folderId, 'isExpanded': instance.isExpanded, diff --git a/lib/model/tokens/push_token.dart b/lib/model/tokens/push_token.dart index 6d68adc56..64b4bf363 100644 --- a/lib/model/tokens/push_token.dart +++ b/lib/model/tokens/push_token.dart @@ -154,7 +154,6 @@ class PushToken extends Token { 'sortIndex: $sortIndex, ' 'pin: $pin, ' 'publicServerKey: $publicServerKey, ' - 'privateTokenKey: $privateTokenKey, ' 'publicTokenKey: $publicTokenKey, ' 'pushRequests: $pushRequests, ' 'tokenImage: $tokenImage, ' diff --git a/lib/state_notifiers/deeplink_notifier.dart b/lib/state_notifiers/deeplink_notifier.dart new file mode 100644 index 000000000..53a1dc700 --- /dev/null +++ b/lib/state_notifiers/deeplink_notifier.dart @@ -0,0 +1,59 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacyidea_authenticator/utils/logger.dart'; +import 'package:uni_links/uni_links.dart'; + +StreamSubscription? _sub; +bool _initialUriIsHandled = false; + +class DeeplinkNotifier extends StateNotifier { + DeeplinkNotifier() : super(null) { + _handleInitialUri(); + _handleIncomingLinks(); + } + + /// Handle incoming links - the ones that the app will recieve from the OS + /// while already started. + void _handleIncomingLinks() { + if (_sub != null) { + _sub?.cancel(); + _sub = null; + } + if (!kIsWeb) { + // It will handle app links while the app is already started - be it in + // the foreground or in the background. + _sub = uriLinkStream.listen((Uri? uri) { + Logger.info('Got uri from listening: $uri'); + if (!mounted) return; + if (uri == null) return; + state = uri; + }, onError: (Object err) { + if (!mounted) return; + Logger.warning('Got error on Uri: $err'); + }); + } + } + + Future _handleInitialUri() async { + if (!_initialUriIsHandled) { + _initialUriIsHandled = true; + Logger.info('_handleInitialUri called'); + try { + final uri = await getInitialUri(); + if (uri == null) return; + Logger.info('Got initial uri: $uri'); + if (!mounted) return; + state = uri; + } on PlatformException { + // Platform messages may fail but we ignore the exception + Logger.warning('falied to get initial uri'); + } on FormatException catch (_) { + if (!mounted) return; + Logger.warning('malformed initial uri'); + } + } + } +} diff --git a/lib/state_notifiers/push_request_notifier.dart b/lib/state_notifiers/push_request_notifier.dart index a480850ad..da2334e41 100644 --- a/lib/state_notifiers/push_request_notifier.dart +++ b/lib/state_notifiers/push_request_notifier.dart @@ -53,10 +53,8 @@ class PushRequestNotifier extends StateNotifier { handleIncomingMessage: (RemoteMessage message) => _handleIncomingAuthRequest(message), backgroundMessageHandler: _firebaseMessagingBackgroundHandler, ); - - if (pollingEnabled) { - PushProvider.pollForChallenges(); - } + Logger.info('PushProvider initialized. Polling for Challenges', name: 'main_screen.dart#_initStateAsync'); + PushProvider.pollForChallenges(); _startOrStopPolling(); } diff --git a/lib/state_notifiers/settings_notifier.dart b/lib/state_notifiers/settings_notifier.dart index fc45465b2..2d679ffe0 100644 --- a/lib/state_notifiers/settings_notifier.dart +++ b/lib/state_notifiers/settings_notifier.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacyidea_authenticator/utils/logger.dart'; import '../model/states/settings_state.dart'; import '../repo/settings_repository.dart'; @@ -20,38 +21,78 @@ class SettingsNotifier extends StateNotifier { } void _loadFromRepo() async { state = await _repo.loadSettings(); + Logger.info('Loading settings from repo: $state', name: 'settings_notifier.dart#_loadFromRepo'); } void _saveToRepo() async { + Logger.info('Saving settings to repo: $state', name: 'settings_notifier.dart#_saveToRepo'); await _repo.saveSettings(state); } void addCrashReportRecipient(String email) { + Logger.info('Crash report recipient added: $email', name: 'settings_notifier.dart#addCrashReportRecipient'); var updatedSet = state.crashReportRecipients..add(email); state = state.copyWith(crashReportRecipients: updatedSet); } - set isFirstRun(bool value) => state = state.copyWith(isFirstRun: value); + set isFirstRun(bool value) { + Logger.info('First run set to $value', name: 'settings_notifier.dart#setFirstRun'); + state = state.copyWith(isFirstRun: value); + } - set hideOTPs(bool value) => state = state.copyWith(hideOpts: value); + set hideOTPs(bool value) { + Logger.info('Hide OTPs set to $value', name: 'settings_notifier.dart#setHideOTPs'); + state = state.copyWith(hideOpts: value); + } - set showGuideOnStart(bool value) => state = state.copyWith(showGuideOnStart: value); + set showGuideOnStart(bool value) { + Logger.info('Show guide on start set to $value', name: 'settings_notifier.dart#setShowGuideOnStart'); + state = state.copyWith(showGuideOnStart: value); + } - void setLocalePreference(Locale locale) => state = state.copyWith(localePreference: locale); + void setLocalePreference(Locale locale) { + Logger.info('Locale set to $locale', name: 'settings_notifier.dart#setLocalePreference'); + state = state.copyWith(localePreference: locale); + } - void setUseSystemLocale(bool value) => state = state.copyWith(useSystemLocale: value); + void setUseSystemLocale(bool value) { + Logger.info('Use system locale set to $value', name: 'settings_notifier.dart#setUseSystemLocale'); + state = state.copyWith(useSystemLocale: value); + } - void enablePolling() => state = state.copyWith(enablePolling: true); + void enablePolling() { + Logger.info('Polling set to true', name: 'settings_notifier.dart#enablePolling'); + state = state.copyWith(enablePolling: true); + } - void disablePolling() => state = state.copyWith(enablePolling: false); + void disablePolling() { + Logger.info('Polling set to false', name: 'settings_notifier.dart#disablePolling'); + state = state.copyWith(enablePolling: false); + } - void setPolling(bool value) => state = state.copyWith(enablePolling: value); + void setPolling(bool value) { + Logger.info('Polling set to $value', name: 'settings_notifier.dart#setPolling'); + state = state.copyWith(enablePolling: value); + } - void setLocale(Locale locale) => state = state.copyWith(localePreference: locale); + void setLocale(Locale locale) { + Logger.info('Locale set to $locale', name: 'settings_notifier.dart#setLocale'); + state = state.copyWith(localePreference: locale); + } - void setVerboseLogging(bool value) => state = state.copyWith(verboseLogging: value); + void setVerboseLogging(bool value) { + Logger.info('Verbose logging set to $value', name: 'settings_notifier.dart#setVerboseLogging'); + state = state.copyWith(verboseLogging: value); + } - void toggleVerboseLogging() => state = state.copyWith(verboseLogging: !state.verboseLogging); + void toggleVerboseLogging() { + final value = !state.verboseLogging; + Logger.info('Verbose logging set to $value', name: 'settings_notifier.dart#setVerboseLogging'); + state = state.copyWith(verboseLogging: value); + } - void setFirstRun(bool value) => state = state.copyWith(isFirstRun: value); + void setFirstRun(bool value) { + Logger.info('First run set to $value', name: 'settings_notifier.dart#setFirstRun'); + state = state.copyWith(isFirstRun: value); + } } diff --git a/lib/state_notifiers/token_notifier.dart b/lib/state_notifiers/token_notifier.dart index 346238a39..67c89b170 100644 --- a/lib/state_notifiers/token_notifier.dart +++ b/lib/state_notifiers/token_notifier.dart @@ -12,6 +12,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http/http.dart'; import 'package:pi_authenticator_legacy/pi_authenticator_legacy.dart'; import 'package:pointycastle/asymmetric/api.dart'; +import 'package:privacyidea_authenticator/model/enums/schemes.dart'; import 'package:privacyidea_authenticator/utils/riverpod_providers.dart'; import '../model/push_request.dart'; @@ -41,9 +42,15 @@ class TokenNotifier extends StateNotifier { Future _loadTokenList() async { List tokens = await StorageUtil.loadAllTokens(); - final pushTokens = tokens.whereType().where((element) => !element.isRolledOut).toList(); + Logger.info('Loaded tokens from storage: $tokens', name: 'token_notifier.dart#_loadTokenList'); + final pushTokens = tokens.whereType(); + if (pushTokens.isNotEmpty) { + checkNotificationPermission(); + } + + final pushTokensNotRolledOut = pushTokens.where((element) => !element.isRolledOut).toList(); state = TokenState(tokens: tokens); - for (final pushToken in pushTokens) { + for (final pushToken in pushTokensNotRolledOut) { rolloutPushToken(pushToken); } } @@ -51,6 +58,7 @@ class TokenNotifier extends StateNotifier { void refreshTokens() async { List tokens = await StorageUtil.loadAllTokens(); final rolledOutPushToken = tokens.whereType().where((element) => element.isRolledOut).toList(); + Logger.info('Refreshed Pushtokens from storage: $tokens', name: 'token_notifier.dart#refreshTokens'); state = state.updateTokens(rolledOutPushToken); } @@ -119,6 +127,23 @@ class TokenNotifier extends StateNotifier { } } + void handleLink(Uri uri) { + if (uri.scheme == enumAsString(UriSchemes.otpauth)) { + addTokenFromOtpAuth(otpAuth: uri.toString(), context: globalNavigatorKey.currentContext!); + return; + } + if (uri.scheme == enumAsString(UriSchemes.pia)) { + addTokenFromPia(pia: uri.toString(), context: globalNavigatorKey.currentContext!); + return; + } + showMessage(message: 'Scheme "${uri.scheme}" is not supported', duration: const Duration(seconds: 3)); + } + + void addTokenFromPia({required String pia, required BuildContext context}) async { + // TODO: Implement pia:// scheme + showMessage(message: 'Scheme "pia" is not implemented yet', duration: const Duration(seconds: 3)); + } + void addTokenFromOtpAuth({required String otpAuth, required BuildContext context}) async { Logger.info( 'Try to handle otpAuth:', @@ -169,7 +194,7 @@ class TokenNotifier extends StateNotifier { Future addPushRequestToToken(PushRequest pr) async { PushToken? token = state.tokens.whereType().firstWhereOrNull((t) => t.serial == pr.serial && t.isRolledOut); - + Logger.info('Adding push request ${pr.id} to token ${token?.id}', name: 'main_screen.dart#_handleIncomingChallenge', error: pr.serial); if (token == null) { Logger.warning('The requested token does not exist or is not rolled out.', name: 'main_screen.dart#_handleIncomingChallenge', error: pr.serial); } else { @@ -230,6 +255,7 @@ class TokenNotifier extends StateNotifier { Future rolloutPushToken(PushToken token) async { token = getTokenFromId(token.id) as PushToken? ?? token; + Logger.info('Rolling out token ${token.serial}', name: 'token_widgets.dart#rolloutPushToken'); if (token.isRolledOut) return true; if (token.rolloutState != PushTokenRollOutState.rolloutNotStarted && token.rolloutState != PushTokenRollOutState.generatingRSAKeyPairFailed && diff --git a/lib/utils/logger.dart b/lib/utils/logger.dart index bbc06c368..dcb4bcb0e 100644 --- a/lib/utils/logger.dart +++ b/lib/utils/logger.dart @@ -13,6 +13,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:logger/logger.dart' as printer; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:privacyidea_authenticator/utils/app_customizer.dart'; import '../views/settings_view/settings_view_widgets/send_error_dialog.dart'; import 'customizations.dart'; @@ -67,8 +68,9 @@ class Logger { bool _flutterIsRunning = false; String get _mailRecipient => 'app-crash@netknights.it'; - String get _mailSubject => - _platformInfos != null ? '(${_platformInfos!.version}) ${_platformInfos!.appName} >>> $_lastError' : 'PrivacyIDEA Authenticator >>> $_lastError'; + String get _mailSubject => _platformInfos != null + ? '(${_platformInfos!.version}+${_platformInfos!.buildNumber}) ${_platformInfos!.appName} >>> $_lastError' + : '${ApplicationCustomizer.appName} >>> $_lastError'; String get _filename => 'logfile.txt'; String? get _fullPath => _logPath != null ? '$_logPath/$_filename' : null; bool get _verbose { diff --git a/lib/utils/push_provider.dart b/lib/utils/push_provider.dart index f26e097ca..fc62a2a50 100644 --- a/lib/utils/push_provider.dart +++ b/lib/utils/push_provider.dart @@ -190,16 +190,18 @@ abstract class PushProvider { List pushTokens = globalRef?.read(tokenProvider).tokens.whereType().where((t) => t.isRolledOut && t.url != null).toList() ?? []; // Disable polling if no push tokens exist - if (pushTokens.isEmpty) { + if (pushTokens.isEmpty && globalRef?.read(settingsProvider).enablePolling == true) { Logger.info('No push token is available for polling, polling is disabled.', name: 'push_provider.dart#pollForChallenges'); globalRef?.read(settingsProvider.notifier).disablePolling(); return null; } // Start request for each token + Logger.info('Polling for challenges: ${pushTokens.length} Tokens', name: 'push_provider.dart#pollForChallenges'); for (PushToken p in pushTokens) { pollForChallenge(p).then((errorMessage) { if (errorMessage != null && showMessageForEachToken) { + Logger.warning(errorMessage, name: 'push_provider.dart#pollForChallenges'); showMessage(message: errorMessage); } }); @@ -240,23 +242,16 @@ abstract class PushProvider { break; case 403: - Logger.warning('Polling push tokens failed with status code ${response.statusCode}', + Logger.warning('Polling push token ${token.serial} failed with status code ${response.statusCode}', name: 'push_provider.dart#pollForChallenge', error: getErrorMessageFromResponse(response)); return null; default: var error = getErrorMessageFromResponse(response); - Logger.warning('Polling push tokens failed with status code ${response.statusCode}', name: 'push_provider.dart#pollForChallenge', error: error); - return "${AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorWhenPullingChallenges}\n$error"; + return "${AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorWhenPullingChallenges(token.serial)}\n$error"; } - } catch (e, s) { - Logger.warning( - 'An error occured when polling for challenges', - name: 'push_provider.dart#pollForChallenge', - error: e, - stackTrace: s, - ); - return "${AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorWhenPullingChallenges}\n${e.toString()}"; + } catch (e) { + return "${AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorWhenPullingChallenges(token.serial)}\n${e.toString()}"; } return null; } diff --git a/lib/utils/riverpod_providers.dart b/lib/utils/riverpod_providers.dart index 1c616a3e8..89f29c2f6 100644 --- a/lib/utils/riverpod_providers.dart +++ b/lib/utils/riverpod_providers.dart @@ -1,5 +1,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacyidea_authenticator/state_notifiers/deeplink_notifier.dart'; +import 'package:privacyidea_authenticator/utils/push_provider.dart'; import '../model/mixins/sortable_mixin.dart'; import '../model/platform_info/platform_info.dart'; @@ -25,24 +27,24 @@ WidgetRef? globalRef; final tokenProvider = StateNotifierProvider((ref) { final tokenNotifier = TokenNotifier(); - Logger.info("appStateProvider.addListener ${tokenNotifier.hashCode.toString()}"); + deeplinkProvider.addListener( + ref.container, + (previous, next) { + if (next == null) return; + Logger.info("tokenProvider received new deeplink"); + tokenNotifier.handleLink(next); + }, + onError: (err, _) => throw err, + onDependencyMayHaveChanged: () {}, + fireImmediately: false, + ); + appStateProvider.addListener( ref.container, (previous, next) { - switch (next) { - case AppState.resume: - //startPolling(); - if (previous == AppState.pause) { - Logger.info('refreshing tokens on resume'); - tokenNotifier.refreshTokens(); - ref.read(appStateProvider.notifier).setAppState(AppState.running); - } - break; - case AppState.pause: - //stopPolling(); - break; - default: - break; + if (previous == AppState.pause && next == AppState.resume) { + Logger.info('Refreshing tokens on resume'); + tokenNotifier.refreshTokens(); } }, onError: (err, stack) { @@ -56,12 +58,13 @@ final tokenProvider = StateNotifierProvider((ref) { ref.container, (previous, next) { if (next == null) return; - Logger.warning('next: $next'); if (next.accepted == null) { + Logger.info("tokenProvider received new pushRequest"); tokenNotifier.addPushRequestToToken(next); return; } if (next.accepted != null) { + Logger.info("tokenProvider received pushRequest with accepted=${next.accepted}... removing it from state."); tokenNotifier.removePushRequest(next); FlutterLocalNotificationsPlugin().cancelAll(); return; @@ -86,9 +89,26 @@ final platformInfoProvider = StateProvider( ); final pushRequestProvider = StateNotifierProvider( - (ref) => PushRequestNotifier(null, pollingEnabled: ref.watch(settingsProvider).enablePolling), + (ref) { + final pushRequestNotifier = PushRequestNotifier(null, pollingEnabled: ref.watch(settingsProvider).enablePolling); + appStateProvider.addListener( + ref.container, + (previous, next) { + if (previous == AppState.pause && next == AppState.resume) { + Logger.info('Polling for challenges on resume'); + PushProvider.pollForChallenges(); + } + }, + onError: (_, __) {}, + onDependencyMayHaveChanged: () {}, + fireImmediately: false, + ); + return pushRequestNotifier; + }, ); +final deeplinkProvider = StateNotifierProvider((ref) => DeeplinkNotifier()); + final appStateProvider = StateNotifierProvider( (ref) => AppStateNotifier(), ); diff --git a/lib/utils/themes.dart b/lib/utils/themes.dart index 5721410bd..4e59b4714 100644 --- a/lib/utils/themes.dart +++ b/lib/utils/themes.dart @@ -38,6 +38,7 @@ var lightThemeData = ThemeData( shadowColor: ApplicationCustomizer.themeColorDark, elevation: 0, ), + floatingActionButtonTheme: const FloatingActionButtonThemeData(elevation: 0), navigationBarTheme: const NavigationBarThemeData().copyWith( backgroundColor: ApplicationCustomizer.themeColorLight, shadowColor: ApplicationCustomizer.themeColorDark, @@ -112,6 +113,7 @@ var darkThemeData = ThemeData( shadowColor: ApplicationCustomizer.themeColorLight, elevation: 0, ), + floatingActionButtonTheme: const FloatingActionButtonThemeData(elevation: 0), navigationBarTheme: const NavigationBarThemeData().copyWith( backgroundColor: ApplicationCustomizer.themeColorDark, shadowColor: ApplicationCustomizer.themeColorLight, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 4579acb32..85f166ba9 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -30,6 +30,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:hex/hex.dart' as HexConverter; import 'package:otp/otp.dart' as OTPLibrary; import 'package:permission_handler/permission_handler.dart'; +import 'package:privacyidea_authenticator/utils/logger.dart'; import 'identifiers.dart'; @@ -84,12 +85,15 @@ bool equalsIgnoreCase(String s1, String s2) { /// If permission is already given, this function does nothing void checkNotificationPermission() async { var status = await Permission.notification.status; + Logger.info('Notification permission status: $status'); // TODO what to do if permanently denied? // Add a dialog before requesting? if (!status.isPermanentlyDenied) { if (status.isDenied) { await Permission.notification.request(); } + } else { + Logger.info('Notification permission is permanently denied!'); } } diff --git a/lib/views/main_view/main_view.dart b/lib/views/main_view/main_view.dart index f8ca9acdd..20b2d638c 100644 --- a/lib/views/main_view/main_view.dart +++ b/lib/views/main_view/main_view.dart @@ -24,15 +24,17 @@ class MainView extends ConsumerStatefulWidget { class _MainViewState extends ConsumerState with LifecycleMixin { final globalKey = GlobalKey(); + @override - void onResume() { - Logger.info('onResume'); + void onAppResume() { + Logger.info('MainView Resume', name: 'main_view.dart#onAppResume'); globalRef?.read(appStateProvider.notifier).setAppState(AppState.resume); + WidgetsBinding.instance.addPostFrameCallback((_) => globalRef?.read(appStateProvider.notifier).setAppState(AppState.running)); } @override - void onPause() { - Logger.info('onPause'); + void onAppPause() { + Logger.info('MainView Pause', name: 'main_view.dart#onAppPause'); globalRef?.read(appStateProvider.notifier).setAppState(AppState.pause); } diff --git a/lib/views/main_view/main_view_widgets/main_view_navigation_buttons.dart b/lib/views/main_view/main_view_widgets/main_view_navigation_buttons.dart index 9fd5db484..723044135 100644 --- a/lib/views/main_view/main_view_widgets/main_view_navigation_buttons.dart +++ b/lib/views/main_view/main_view_widgets/main_view_navigation_buttons.dart @@ -65,7 +65,7 @@ class MainViewNavigationButtions extends StatelessWidget { height: size.height * 0.3, ), applicationLegalese: ApplicationCustomizer.websiteLink, - applicationVersion: globalRef?.read(platformInfoProvider).appVersion, + applicationVersion: '${globalRef?.read(platformInfoProvider).appVersion}+${globalRef?.read(platformInfoProvider).buildNumber}', ), ), ); diff --git a/lib/views/main_view/main_view_widgets/token_widgets/day_password_token_widgets/day_password_token_widget_tile.dart b/lib/views/main_view/main_view_widgets/token_widgets/day_password_token_widgets/day_password_token_widget_tile.dart index 644131222..3701b1732 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/day_password_token_widgets/day_password_token_widget_tile.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/day_password_token_widgets/day_password_token_widget_tile.dart @@ -24,16 +24,21 @@ class DayPasswordTokenWidgetTile extends ConsumerStatefulWidget { class _DayPasswordTokenWidgetTileState extends ConsumerState { double secondsLeft = 0; + late DateTime lastCount; final ValueNotifier isHidden = ValueNotifier(true); @override void initState() { super.initState(); secondsLeft = widget.token.durationUntilNextOTP.inMilliseconds / 1000; - _countDown(0); + lastCount = DateTime.now(); + _startCountDown(); } - void _countDown(int msSinceLastCount) { + void _startCountDown() { + final now = DateTime.now(); + final msSinceLastCount = now.difference(lastCount).inMilliseconds; + lastCount = now; if (!mounted) return; if (secondsLeft - (msSinceLastCount / 1000) > 0) { setState(() => secondsLeft -= msSinceLastCount / 1000); @@ -41,11 +46,7 @@ class _DayPasswordTokenWidgetTileState extends ConsumerState secondsLeft = widget.token.durationUntilNextOTP.inMilliseconds / 1000); } final msUntilNextSecond = (secondsLeft * 1000).toInt() % 1000 + 1; // +1 to avoid 0 - Future.delayed( - Duration( - milliseconds: msUntilNextSecond, - ), - () => _countDown(msUntilNextSecond)); + Future.delayed(Duration(milliseconds: msUntilNextSecond), () => _startCountDown()); } void _copyOtpValue() { diff --git a/lib/views/main_view/main_view_widgets/token_widgets/totp_token_widgets/totp_token_widget_tile.dart b/lib/views/main_view/main_view_widgets/token_widgets/totp_token_widgets/totp_token_widget_tile.dart index 61cf27a5e..ecdc44fb3 100644 --- a/lib/views/main_view/main_view_widgets/token_widgets/totp_token_widgets/totp_token_widget_tile.dart +++ b/lib/views/main_view/main_view_widgets/token_widgets/totp_token_widgets/totp_token_widget_tile.dart @@ -24,6 +24,7 @@ class TOTPTokenWidgetTile extends ConsumerStatefulWidget { class _TOTPTokenWidgetTileState extends ConsumerState with SingleTickerProviderStateMixin { double secondsLeft = 0; late AnimationController animation; + late DateTime lastCount; final ValueNotifier isHidden = ValueNotifier(true); void _copyOtpValue() { @@ -49,13 +50,14 @@ class _TOTPTokenWidgetTileState extends ConsumerState with duration: Duration(seconds: widget.token.period), ); animation.forward(from: 1 - widget.token.secondsUntilNextOTP / widget.token.period); - _countDown(0); - ref.read(appStateProvider.notifier).addListener(_onAppStateChange); + secondsLeft = widget.token.secondsUntilNextOTP; + lastCount = DateTime.now(); + _startCountDown(); isHidden.addListener(() { if (mounted) { setState(() { if (isHidden.value == false) { - Future.delayed(Duration(milliseconds: (widget.token.period * 1000 + (widget.token.secondsUntilNextOTP * 1000).toInt())), () { + Future.delayed(Duration(milliseconds: (widget.token.period * 1000 + (secondsLeft * 1000).toInt())), () { isHidden.value = true; }); } @@ -83,16 +85,26 @@ class _TOTPTokenWidgetTileState extends ConsumerState with super.dispose(); } - void _countDown(int msSinceLastCount) { + void _startCountDown() { + final now = DateTime.now(); + final msSinceLastCount = now.difference(lastCount).inMilliseconds; + lastCount = now; if (!mounted) return; - setState(() => secondsLeft = widget.token.secondsUntilNextOTP); - animation.forward(from: 1 - secondsLeft / widget.token.period); + if (secondsLeft - (msSinceLastCount / 1000) > 0) { + setState(() => secondsLeft -= msSinceLastCount / 1000); + } else { + setState(() => secondsLeft = widget.token.secondsUntilNextOTP); + animation.forward(from: 1 - secondsLeft / widget.token.period); + } + final msUntilNextSecond = (secondsLeft * 1000).toInt() % 1000 + 1; // +1 to avoid 0 - Future.delayed(Duration(milliseconds: msUntilNextSecond), () => _countDown(msUntilNextSecond)); + Future.delayed(Duration(milliseconds: msUntilNextSecond), () => _startCountDown()); } @override Widget build(BuildContext context) { + final appstate = ref.watch(appStateProvider); + _onAppStateChange(appstate); return TokenWidgetTile( key: Key('${widget.token.hashCode}TokenWidgetTile'), tokenImage: widget.token.tokenImage, @@ -130,11 +142,11 @@ class _TOTPTokenWidgetTileState extends ConsumerState with children: [ Center( child: Text( - '${secondsLeft.ceil()}', + '${secondsLeft.round()}', overflow: TextOverflow.fade, softWrap: false, ), - ), // ceil to show 30 -> 1 instead of 29 -> 0 + ), Center( child: CircularProgressIndicator( value: animation.value, diff --git a/pubspec.lock b/pubspec.lock index a0a91d488..3550349b0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "1a5e13736d59235ce0139621b4bbe29bc89839e202409081bc667eb3cd20674c" + sha256: "2d8e8e123ca3675625917f535fcc0d3a50092eef44334168f9b18adc050d4c6e" url: "https://pub.dev" source: hosted - version: "1.3.5" + version: "1.3.6" analyzer: dependency: transitive description: @@ -29,10 +29,10 @@ packages: dependency: transitive description: name: archive - sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" + sha256: e0902a06f0e00414e4e3438a084580161279f137aeb862274710f29ec10cf01e url: "https://pub.dev" source: hosted - version: "3.3.7" + version: "3.3.9" args: dependency: transitive description: @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: build_resolvers - sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" + sha256: d912852cce27c9e80a93603db721c267716894462e7033165178b91138587972 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.2" build_runner: dependency: "direct dev" description: @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: built_value - sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" + sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 url: "https://pub.dev" source: hosted - version: "8.6.1" + version: "8.6.3" characters: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" + sha256: "315a598c7fbe77f22de1c9da7cfd6fd21816312f16ffa124453b4fc679e540f1" url: "https://pub.dev" source: hosted - version: "4.5.0" + version: "4.6.0" collection: dependency: "direct main" description: @@ -229,10 +229,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.6" dart_style: dependency: transitive description: @@ -317,10 +317,10 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: c78132175edda4bc532a71e01a32964e4b4fcf53de7853a422d96dac3725f389 + sha256: "675c209c94a1817649137cbd113fc4c9ae85e48d03dd578629abbec6d8a4d93d" url: "https://pub.dev" source: hosted - version: "2.15.1" + version: "2.16.0" firebase_core_platform_interface: dependency: transitive description: @@ -333,34 +333,34 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: "4cf4d2161530332ddc3c562f19823fb897ff37a9a774090d28df99f47370e973" + sha256: e8c408923cd3a25bd342c576a114f2126769cd1a57106a4edeaa67ea4a84e962 url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.8.0" firebase_messaging: dependency: "direct main" description: name: firebase_messaging - sha256: db4a38be54fd84849c21be1ae1b44f0d4637eec1069bf5c49ea95e81f582bbc0 + sha256: "4544524c22de3ffdc7e0ffaeeba212a04d09e76d0549ae6f42ce285d9d8f0513" url: "https://pub.dev" source: hosted - version: "14.6.6" + version: "14.6.8" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface - sha256: "164119eed47ff19284e28bea9165a03da110c56ea09dd996622cfccad14d0efd" + sha256: a6e1fae8242a14d5d8f5ab1cf94693511f06bab49ff1d46e3d83c0af3c4becb8 url: "https://pub.dev" source: hosted - version: "4.5.5" + version: "4.5.7" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web - sha256: "6196d20731733834d7afb175c4345be57ddbd5daebca83cd52a430d62c2279fe" + sha256: a9fe837dc2dcdd3e32e6109a6b0ce62592d7a44cb8f69cb5b73190865c5aa28e url: "https://pub.dev" source: hosted - version: "3.5.5" + version: "3.5.7" fixnum: dependency: transitive description: @@ -399,18 +399,18 @@ packages: dependency: "direct main" description: name: flutter_lints - sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.0.3" flutter_local_notifications: dependency: "direct main" description: name: flutter_local_notifications - sha256: "3cc40fe8c50ab8383f3e053a499f00f975636622ecdc8e20a77418ece3b1e975" + sha256: "3002092e5b8ce2f86c3361422e52e6db6776c23ee21e0b2f71b892bf4259ef04" url: "https://pub.dev" source: hosted - version: "15.1.0+1" + version: "15.1.1" flutter_local_notifications_linux: dependency: transitive description: @@ -444,74 +444,74 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "2b206d397dd7836ea60035b2d43825c8a303a76a5098e66f42d55a753e18d431" + sha256: a10979814c5f4ddbe2b6143fba25d927599e21e3ba65b3862995960606fae78f url: "https://pub.dev" source: hosted - version: "0.6.17+1" + version: "0.6.17+3" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360" + sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.0.16" flutter_riverpod: dependency: "direct main" description: name: flutter_riverpod - sha256: b6cb0041c6c11cefb2dcb97ef436eba43c6d41287ac6d8ca93e02a497f53a4f3 + sha256: "1bd39b04f1bcd217a969589777ca6bd642d116e3e5de65c3e6a8e8bdd8b178ec" url: "https://pub.dev" source: hosted - version: "2.3.7" + version: "2.4.0" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage - sha256: "98352186ee7ad3639ccc77ad7924b773ff6883076ab952437d20f18a61f0a7c5" + sha256: ffdbb60130e4665d2af814a0267c481bcf522c41ae2e43caf69fa0146876d685 url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "9.0.0" flutter_secure_storage_linux: dependency: transitive description: name: flutter_secure_storage_linux - sha256: "0912ae29a572230ad52d8a4697e5518d7f0f429052fd51df7e5a7952c7efe2a3" + sha256: "3d5032e314774ee0e1a7d0a9f5e2793486f0dff2dd9ef5a23f4e3fb2a0ae6a9e" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.2.0" flutter_secure_storage_macos: dependency: transitive description: name: flutter_secure_storage_macos - sha256: "083add01847fc1c80a07a08e1ed6927e9acd9618a35e330239d4422cd2a58c50" + sha256: bd33935b4b628abd0b86c8ca20655c5b36275c3a3f5194769a7b3f37c905369c url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "3.0.1" flutter_secure_storage_platform_interface: dependency: transitive description: name: flutter_secure_storage_platform_interface - sha256: b3773190e385a3c8a382007893d678ae95462b3c2279e987b55d140d3b0cb81b + sha256: "0d4d3a5dd4db28c96ae414d7ba3b8422fd735a8255642774803b2532c9a61d7e" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.0.2" flutter_secure_storage_web: dependency: transitive description: name: flutter_secure_storage_web - sha256: "42938e70d4b872e856e678c423cc0e9065d7d294f45bc41fc1981a4eb4beaffe" + sha256: "30f84f102df9dcdaa2241866a958c2ec976902ebdaa8883fbfe525f1f2f3cf20" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" flutter_secure_storage_windows: dependency: transitive description: name: flutter_secure_storage_windows - sha256: fc2910ec9b28d60598216c29ea763b3a96c401f0ce1d13cdf69ccb0e5c93c3ee + sha256: "5809c66f9dd3b4b93b0a6e2e8561539405322ee767ac2f64d084e2ab5429d108" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "3.0.0" flutter_slidable: dependency: "direct main" description: @@ -534,10 +534,10 @@ packages: dependency: "direct main" description: name: flutterlifecyclehooks - sha256: "16e143ea8d549afd54f214661ef5c20ca58e8366ea0923b1a4d5f3ff22e2fc43" + sha256: "25c16ff88513445c049735013ed66ddd364530c9467b5784a5667ee0e644dd67" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "4.0.0" fluttertoast: dependency: "direct main" description: @@ -659,10 +659,10 @@ packages: dependency: "direct main" description: name: lifecycle - sha256: "460440bf148efc9a01f52b08758b7c6fd1d0d60beb7c2fb1cc714823508094b9" + sha256: ad96a411440e8ab5b8dd63eb6345b1385863f33a09ae073e0ea110159ea81787 url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.8.0" lints: dependency: transitive description: @@ -675,50 +675,50 @@ packages: dependency: "direct main" description: name: local_auth - sha256: "0cf238be2bfa51a6c9e7e9cfc11c05ea39f2a3a4d3e5bb255d0ebc917da24401" + sha256: "7e6c63082e399b61e4af71266b012e767a5d4525dd6e9ba41e174fd42d76e115" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" local_auth_android: dependency: "direct main" description: name: local_auth_android - sha256: "36a78898198386d36d4e152b8cb46059b18f0e2017f813a0e833e216199f8950" + sha256: "9ad0b1ffa6f04f4d91e38c2d4c5046583e23f4cae8345776a994e8670df57fb1" url: "https://pub.dev" source: hosted - version: "1.0.32" + version: "1.0.34" local_auth_ios: dependency: "direct main" description: name: local_auth_ios - sha256: edc2977c5145492f3451db9507a2f2f284ee4f408950b3e16670838726761940 + sha256: "26a8d1ad0b4ef6f861d29921be8383000fda952e323a5b6752cf82ca9cf9a7a9" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.4" local_auth_platform_interface: dependency: transitive description: name: local_auth_platform_interface - sha256: "9e160d59ef0743e35f1b50f4fb84dc64f55676b1b8071e319ef35e7f3bc13367" + sha256: fc5bd537970a324260fda506cfb61b33ad7426f37a8ea5c461cf612161ebba54 url: "https://pub.dev" source: hosted - version: "1.0.7" + version: "1.0.8" local_auth_windows: dependency: transitive description: name: local_auth_windows - sha256: "5af808e108c445d0cf702a8c5f8242f1363b7970320334f82e6e1e8ad0b0d7d4" + sha256: "505ba3367ca781efb1c50d3132e44a2446bccc4163427bc203b9b4d8994d97ea" url: "https://pub.dev" source: hosted - version: "1.0.9" + version: "1.0.10" logger: dependency: "direct main" description: name: logger - sha256: "66cb048220ca51cf9011da69fa581e4ee2bed4be6e82870d9e9baae75739da49" + sha256: ba3bc83117b2b49bdd723c0ea7848e8285a0fbc597ba09203b20d329d020c24a url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.2" logging: dependency: transitive description: @@ -859,66 +859,66 @@ packages: dependency: "direct main" description: name: path_provider - sha256: "909b84830485dbcd0308edf6f7368bc8fd76afa26a270420f34cabea2a6467a0" + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "5d44fc3314d969b84816b569070d7ace0f1dea04bd94a83f74c4829615d22ad8" + sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "1b744d3d774e5a879bb76d6cd1ecee2ba2c6960c03b1020cd35212f6aa267ac5" + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" path_provider_linux: dependency: transitive description: name: path_provider_linux - sha256: ba2b77f0c52a33db09fc8caf85b12df691bf28d983e84cf87ff6d693cfa007b3 + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface - sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84 + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" path_provider_windows: dependency: transitive description: name: path_provider_windows - sha256: ee0e0d164516b90ae1f970bdf29f726f1aa730d7cfc449ecc74c495378b705da + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" permission_handler: dependency: "direct main" description: name: permission_handler - sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81" + sha256: ad65ba9af42a3d067203641de3fd9f547ded1410bad3b84400c2b4899faede70 url: "https://pub.dev" source: hosted - version: "10.4.3" + version: "11.0.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "2ffaf52a21f64ac9b35fe7369bb9533edbd4f698e5604db8645b1064ff4cf221" + sha256: "740c9c9d3090a29cd2fd1f193bba457ac1d2cef1da0ef91f2630d3d9e3c01c15" url: "https://pub.dev" source: hosted - version: "10.3.3" + version: "11.0.2" permission_handler_apple: dependency: transitive description: @@ -931,10 +931,10 @@ packages: dependency: transitive description: name: permission_handler_platform_interface - sha256: "7c6b1500385dd1d2ca61bb89e2488ca178e274a69144d26bbd65e33eae7c02a9" + sha256: f2343e9fa9c22ae4fd92d4732755bfe452214e7189afcc097380950cf567b4b2 url: "https://pub.dev" source: hosted - version: "3.11.3" + version: "3.11.5" permission_handler_windows: dependency: transitive description: @@ -970,10 +970,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" + sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.1.6" pointycastle: dependency: "direct main" description: @@ -1026,66 +1026,66 @@ packages: dependency: transitive description: name: riverpod - sha256: b0657b5b30c81a3184bdaab353045f0a403ebd60bb381591a8b7ad77dcade793 + sha256: a600120d6f213a9922860eea1abc32597436edd5b2c4e73b91410f8c2af67d22 url: "https://pub.dev" source: hosted - version: "2.3.7" + version: "2.4.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1" + sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076 + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: d29753996d8eb8f7619a1f13df6ce65e34bc107bef6330739ed76f18b22310ef + sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.4" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1" + sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1" + sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a" + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d + sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.1" shelf: dependency: transitive description: @@ -1163,6 +1163,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -1303,74 +1311,74 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e" + sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" url: "https://pub.dev" source: hosted - version: "6.1.12" + version: "6.1.14" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "3dd2388cc0c42912eee04434531a26a82512b9cb1827e0214430c9bcbddfe025" + sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 url: "https://pub.dev" source: hosted - version: "6.0.38" + version: "6.1.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "9af7ea73259886b92199f9e42c116072f05ff9bea2dcb339ab935dfc957392c2" + sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "6.1.5" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "207f4ddda99b95b4d4868320a352d374b0b7e05eefad95a4a26f57da413443f5" + sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.6" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1" + sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea + sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" url: "https://pub.dev" source: hosted - version: "2.1.3" + version: "2.1.5" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4 + sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5" url: "https://pub.dev" source: hosted - version: "2.0.18" + version: "2.0.20" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422" + sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.0.8" uuid: dependency: "direct main" description: name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + sha256: e03928880bdbcbf496fb415573f5ab7b1ea99b9b04f669c01104d085893c3134 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "4.0.0" vector_math: dependency: transitive description: @@ -1431,34 +1439,34 @@ packages: dependency: transitive description: name: webkit_inspection_protocol - sha256: "67d3a8b6c79e1987d19d848b0892e582dbb0c66c57cc1fef58a177dd2aa2823d" + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" win32: dependency: transitive description: name: win32 - sha256: f2add6fa510d3ae152903412227bda57d0d5a8da61d2c39c1fb022c9429a41c0 + sha256: c97defd418eef4ec88c0d1652cdce84b9f7b63dd7198e266d06ac1710d527067 url: "https://pub.dev" source: hosted - version: "5.0.6" + version: "5.0.8" win32_registry: dependency: transitive description: name: win32_registry - sha256: e4506d60b7244251bc59df15656a3093501c37fb5af02105a944d73eb95be4c9 + sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: f0c26453a2d47aa4c2570c6a033246a3fc62da2fe23c7ffdd0a7495086dc0247 + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "1.0.3" xml: dependency: transitive description: @@ -1476,5 +1484,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" - flutter: ">=3.10.0" + dart: ">=3.1.0 <4.0.0" + flutter: ">=3.13.0" diff --git a/pubspec.yaml b/pubspec.yaml index dfe9ba85b..5ab116668 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ publish_to: none # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 4.2.0+402015 # TODO Set the right version number +version: 4.2.0+402018 # TODO Set the right version number # version: major.minor.build + 2x major|2x minor|3x build # version: version number + build number (optional) # android: build-name + versionCode @@ -35,7 +35,7 @@ dependencies: base32: ^2.1.1 otp: ^3.0.1 qr_mobile_vision: ^4.1.3 - flutter_secure_storage: ^8.0.0 + flutter_secure_storage: ^9.0.0 json_annotation: ^4.8.1 flutter_slidable: ^3.0.0 package_info_plus: ^4.0.2 @@ -43,13 +43,13 @@ dependencies: flutter_markdown: ^0.6.8 url_launcher: ^6.0.12 fluttertoast: ^8.2.1 - uuid: ^3.0.5 + uuid: ^4.0.0 http: ^1.1.0 pointycastle: ^3.4.0 mutex: ^3.0.0 flutter_lints: ^2.0.2 expandable: ^5.0.1 - flutterlifecyclehooks: ^3.0.2 + flutterlifecyclehooks: ^4.0.0 streaming_shared_preferences: ^2.0.0 easy_dynamic_theme: ^2.2.0 firebase_messaging: ^14.6.4 @@ -62,12 +62,12 @@ dependencies: lottie: ^2.4.0 flare_flutter: ^3.0.2 flutter_mailer: ^2.1.1 - permission_handler: ^10.2.0 + permission_handler: ^11.0.0 path_provider: ^2.0.15 logger: ^2.0.0 flutter_riverpod: ^2.3.6 shared_preferences: ^2.2.0 - lifecycle: ^0.7.0 + lifecycle: ^0.8.0 flutter_launcher_icons: ^0.13.1 flutter_local_notifications: ^15.1.0+1 extended_nested_scroll_view: ^6.1.2