diff --git a/.github/workflows/flutter_build.yml b/.github/workflows/flutter_build.yml index b5d892e76..dd19244aa 100644 --- a/.github/workflows/flutter_build.yml +++ b/.github/workflows/flutter_build.yml @@ -31,7 +31,7 @@ jobs: - run: "flutter upgrade" - run: "flutter --version" - run: "flutter pub get" - - run: "flutter build ios -t 'lib/main_netknights.dart' --no-codesign" + - run: "flutter build ios -t 'lib/mains/main_netknights.dart' --debug --flavor netknights --no-codesign" build_appbundle: name: (Android) @@ -45,17 +45,17 @@ jobs: api-level: [ 21, 30, 34 ] # [minSdk, most used, newest] target: [ default ] # [default, google_apis] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-java@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: + distribution: 'zulu' java-version: '17.0.7' - uses: subosito/flutter-action@v2 with: channel: 'stable' - flutter-version: '3.10.5' + flutter-version: '3.19.0' - run: "flutter upgrade" - run: "flutter --version" - run: "flutter pub get" - run: 'flutter clean' - - run: "flutter build apk -t 'lib/main_netknights.dart' --debug" - + - run: "flutter build apk -t 'lib/mains/main_netknights.dart' --debug --flavor netknights" \ No newline at end of file diff --git a/.github/workflows/flutter_test.yml b/.github/workflows/flutter_test.yml index a10d35310..200b861d4 100644 --- a/.github/workflows/flutter_test.yml +++ b/.github/workflows/flutter_test.yml @@ -11,15 +11,15 @@ jobs: timeout-minutes: 40 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: distribution: 'zulu' - java-version: '11' + java-version: '17.0.7' - uses: subosito/flutter-action@v2 with: channel: 'stable' - flutter-version: '3.16.5' + flutter-version: '3.19.0' - run: "flutter --version" - run: flutter pub get - run: flutter test diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index b46db6b52..a25fe36b6 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -11,15 +11,15 @@ jobs: matrix: api-level: [29] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: distribution: 'zulu' - java-version: '11' + java-version: '17.0.7' - uses: subosito/flutter-action@v2 with: - flutter-version: '3.16.5' + flutter-version: '3.19.0' channel: 'stable' # Run integration test @@ -29,5 +29,5 @@ jobs: api-level: ${{ matrix.api-level }} arch: x86_64 profile: Nexus 6 - script: flutter test integration_test + script: flutter test integration_test --flavor netknights diff --git a/integration_test/add_tokens_test.dart b/integration_test/add_tokens_test.dart index 907332659..9ede5320c 100644 --- a/integration_test/add_tokens_test.dart +++ b/integration_test/add_tokens_test.dart @@ -6,8 +6,11 @@ import 'package:privacyidea_authenticator/l10n/app_localizations_en.dart'; import 'package:privacyidea_authenticator/mains/main_netknights.dart'; import 'package:privacyidea_authenticator/model/enums/algorithms.dart'; import 'package:privacyidea_authenticator/model/enums/encodings.dart'; +import 'package:privacyidea_authenticator/model/enums/introduction.dart'; import 'package:privacyidea_authenticator/model/enums/token_types.dart'; +import 'package:privacyidea_authenticator/model/states/introduction_state.dart'; import 'package:privacyidea_authenticator/model/states/settings_state.dart'; +import 'package:privacyidea_authenticator/state_notifiers/completed_introduction_notifier.dart'; import 'package:privacyidea_authenticator/state_notifiers/settings_notifier.dart'; import 'package:privacyidea_authenticator/state_notifiers/token_folder_notifier.dart'; import 'package:privacyidea_authenticator/state_notifiers/token_notifier.dart'; @@ -31,6 +34,7 @@ void main() { late final MockSettingsRepository mockSettingsRepository; late final MockTokenRepository mockTokenRepository; late final MockTokenFolderRepository mockTokenFolderRepository; + late final MockIntroductionRepository mockIntroductionRepository; setUp(() { mockSettingsRepository = MockSettingsRepository(); when(mockSettingsRepository.loadSettings()).thenAnswer((_) async => SettingsState(useSystemLocale: false, localePreference: const Locale('en'))); @@ -42,6 +46,9 @@ void main() { mockTokenFolderRepository = MockTokenFolderRepository(); when(mockTokenFolderRepository.loadFolders()).thenAnswer((_) async => []); when(mockTokenFolderRepository.saveOrReplaceFolders(any)).thenAnswer((_) async => []); + mockIntroductionRepository = MockIntroductionRepository(); + final introductions = {...Introduction.values}..remove(Introduction.introductionScreen); + when(mockIntroductionRepository.loadCompletedIntroductions()).thenAnswer((_) async => IntroductionState(completedIntroductions: introductions)); }); testWidgets( 'Add Tokens Test', @@ -51,6 +58,7 @@ void main() { settingsProvider.overrideWith((ref) => SettingsNotifier(repository: mockSettingsRepository)), tokenProvider.overrideWith((ref) => TokenNotifier(repository: mockTokenRepository)), tokenFolderProvider.overrideWith((ref) => TokenFolderNotifier(repository: mockTokenFolderRepository)), + introductionProvider.overrideWith((ref) => InrtroductionNotifier(repository: mockIntroductionRepository)), ], child: PrivacyIDEAAuthenticator(customization: ApplicationCustomization.defaultCustomization), )); @@ -82,13 +90,17 @@ void main() { } Future _introToMainView(WidgetTester tester) async { - final Finder finder = find.byType(FloatingActionButton); + var finder = find.byType(FloatingActionButton); + await pumpUntilFindNWidgets(tester, finder, 1, const Duration(seconds: 20)); + await tester.tap(finder); + await tester.pump(const Duration(milliseconds: 2000)); + await tester.tap(finder); + await tester.pump(const Duration(milliseconds: 2000)); + await tester.tap(finder); + await tester.pump(const Duration(milliseconds: 2000)); + finder = find.text(AppLocalizationsEn().ok); await pumpUntilFindNWidgets(tester, finder, 1, const Duration(seconds: 10)); - await tester.tap(find.byType(FloatingActionButton)); - await tester.pump(const Duration(milliseconds: 1000)); - await tester.tap(find.byType(FloatingActionButton)); - await tester.pump(const Duration(milliseconds: 1000)); - await tester.tap(find.byType(FloatingActionButton)); + await tester.tap(finder); await tester.pump(const Duration(milliseconds: 1000)); } @@ -160,7 +172,7 @@ Future _createFolder(WidgetTester tester) async { await tester.pump(const Duration(milliseconds: 1000)); await tester.enterText(find.byType(TextField).first, AppLocalizationsEn().folderName); await tester.pump(); - await tester.tap(find.text(AppLocalizationsEn().save)); + await tester.tap(find.text(AppLocalizationsEn().create)); await tester.pump(); } @@ -208,7 +220,7 @@ Future _openFolder(WidgetTester tester) async { void expectMainViewIsEmptyAndCorrect() { expect(find.byType(FloatingActionButton), findsOneWidget); - expect(find.byType(AppBarItem), findsNWidgets(4)); + expect(find.byType(AppBarItem), findsNWidgets(5)); // 4 at BottomNavigationBar and 1 at AppBar expect(find.byType(TokenWidgetBase), findsNothing); expect(find.byType(TokenFolderWidget), findsNothing); expect(find.text(ApplicationCustomization.defaultCustomization.appName), findsOneWidget); diff --git a/integration_test/copy_to_clipboard_test.dart b/integration_test/copy_to_clipboard_test.dart index 795b3eca8..3afe300b4 100644 --- a/integration_test/copy_to_clipboard_test.dart +++ b/integration_test/copy_to_clipboard_test.dart @@ -5,6 +5,8 @@ import 'package:integration_test/integration_test.dart'; import 'package:mockito/mockito.dart'; import 'package:privacyidea_authenticator/mains/main_netknights.dart'; import 'package:privacyidea_authenticator/model/enums/algorithms.dart'; +import 'package:privacyidea_authenticator/model/enums/introduction.dart'; +import 'package:privacyidea_authenticator/model/states/introduction_state.dart'; import 'package:privacyidea_authenticator/model/states/settings_state.dart'; import 'package:privacyidea_authenticator/model/tokens/hotp_token.dart'; import 'package:privacyidea_authenticator/state_notifiers/settings_notifier.dart'; @@ -12,6 +14,7 @@ import 'package:privacyidea_authenticator/state_notifiers/token_folder_notifier. import 'package:privacyidea_authenticator/state_notifiers/token_notifier.dart'; import 'package:privacyidea_authenticator/utils/app_customizer.dart'; import 'package:privacyidea_authenticator/utils/riverpod_providers.dart'; +import 'package:privacyidea_authenticator/utils/version.dart'; import '../test/tests_app_wrapper.dart'; import '../test/tests_app_wrapper.mocks.dart'; @@ -21,10 +24,11 @@ void main() { late final MockSettingsRepository mockSettingsRepository; late final MockTokenRepository mockTokenRepository; late final MockTokenFolderRepository mockTokenFolderRepository; + late final MockIntroductionRepository mockIntroductionRepository; setUp(() { mockSettingsRepository = MockSettingsRepository(); - when(mockSettingsRepository.loadSettings()) - .thenAnswer((_) async => SettingsState(isFirstRun: false, useSystemLocale: false, localePreference: const Locale('en'))); + when(mockSettingsRepository.loadSettings()).thenAnswer((_) async => + SettingsState(isFirstRun: false, useSystemLocale: false, localePreference: const Locale('en'), latestVersion: Version.parse('999.999.999'))); when(mockSettingsRepository.saveSettings(any)).thenAnswer((_) async => true); mockTokenRepository = MockTokenRepository(); when(mockTokenRepository.loadTokens()).thenAnswer((_) async => [ @@ -35,6 +39,9 @@ void main() { mockTokenFolderRepository = MockTokenFolderRepository(); when(mockTokenFolderRepository.loadFolders()).thenAnswer((_) async => []); when(mockTokenFolderRepository.saveOrReplaceFolders(any)).thenAnswer((_) async => []); + mockIntroductionRepository = MockIntroductionRepository(); + final introductions = {...Introduction.values}..remove(Introduction.introductionScreen); + when(mockIntroductionRepository.loadCompletedIntroductions()).thenAnswer((_) async => IntroductionState(completedIntroductions: introductions)); }); testWidgets('Copy to Clipboard Test', (tester) async { await tester.pumpWidget(TestsAppWrapper( diff --git a/integration_test/rename_and_delete_test.dart b/integration_test/rename_and_delete_test.dart index aecce29a4..f7c901d4e 100644 --- a/integration_test/rename_and_delete_test.dart +++ b/integration_test/rename_and_delete_test.dart @@ -5,6 +5,8 @@ import 'package:mockito/mockito.dart'; import 'package:privacyidea_authenticator/l10n/app_localizations_en.dart'; import 'package:privacyidea_authenticator/mains/main_netknights.dart'; import 'package:privacyidea_authenticator/model/enums/algorithms.dart'; +import 'package:privacyidea_authenticator/model/enums/introduction.dart'; +import 'package:privacyidea_authenticator/model/states/introduction_state.dart'; import 'package:privacyidea_authenticator/model/states/settings_state.dart'; import 'package:privacyidea_authenticator/model/tokens/hotp_token.dart'; import 'package:privacyidea_authenticator/state_notifiers/settings_notifier.dart'; @@ -12,6 +14,7 @@ import 'package:privacyidea_authenticator/state_notifiers/token_folder_notifier. import 'package:privacyidea_authenticator/state_notifiers/token_notifier.dart'; import 'package:privacyidea_authenticator/utils/app_customizer.dart'; import 'package:privacyidea_authenticator/utils/riverpod_providers.dart'; +import 'package:privacyidea_authenticator/utils/version.dart'; import 'package:privacyidea_authenticator/views/main_view/main_view_widgets/token_widgets/default_token_actions/default_delete_action.dart'; import 'package:privacyidea_authenticator/views/main_view/main_view_widgets/token_widgets/hotp_token_widgets/actions/edit_hotp_token_action.dart'; import 'package:privacyidea_authenticator/views/main_view/main_view_widgets/token_widgets/hotp_token_widgets/hotp_token_widget.dart'; @@ -24,10 +27,11 @@ void main() { late final MockSettingsRepository mockSettingsRepository; late final MockTokenRepository mockTokenRepository; late final MockTokenFolderRepository mockTokenFolderRepository; + late final MockIntroductionRepository mockIntroductionRepository; setUp(() { mockSettingsRepository = MockSettingsRepository(); - when(mockSettingsRepository.loadSettings()) - .thenAnswer((_) async => SettingsState(isFirstRun: false, useSystemLocale: false, localePreference: const Locale('en'))); + when(mockSettingsRepository.loadSettings()).thenAnswer((_) async => + SettingsState(isFirstRun: false, useSystemLocale: false, localePreference: const Locale('en'), latestVersion: Version.parse('999.999.999'))); when(mockSettingsRepository.saveSettings(any)).thenAnswer((_) async => true); mockTokenRepository = MockTokenRepository(); when(mockTokenRepository.loadTokens()).thenAnswer((_) async => [ @@ -38,6 +42,9 @@ void main() { mockTokenFolderRepository = MockTokenFolderRepository(); when(mockTokenFolderRepository.loadFolders()).thenAnswer((_) async => []); when(mockTokenFolderRepository.saveOrReplaceFolders(any)).thenAnswer((_) async => []); + mockIntroductionRepository = MockIntroductionRepository(); + final introductions = {...Introduction.values}..remove(Introduction.introductionScreen); + when(mockIntroductionRepository.loadCompletedIntroductions()).thenAnswer((_) async => IntroductionState(completedIntroductions: introductions)); }); testWidgets('Rename and Delete Token', (tester) async { await tester.pumpWidget(TestsAppWrapper( diff --git a/integration_test/two_step_rollout_test.dart b/integration_test/two_step_rollout_test.dart index f10d2368d..dc61107e3 100644 --- a/integration_test/two_step_rollout_test.dart +++ b/integration_test/two_step_rollout_test.dart @@ -4,6 +4,8 @@ import 'package:integration_test/integration_test.dart'; import 'package:mockito/mockito.dart'; import 'package:privacyidea_authenticator/l10n/app_localizations_en.dart'; import 'package:privacyidea_authenticator/mains/main_netknights.dart'; +import 'package:privacyidea_authenticator/model/enums/introduction.dart'; +import 'package:privacyidea_authenticator/model/states/introduction_state.dart'; import 'package:privacyidea_authenticator/model/states/settings_state.dart'; import 'package:privacyidea_authenticator/state_notifiers/settings_notifier.dart'; import 'package:privacyidea_authenticator/state_notifiers/token_folder_notifier.dart'; @@ -11,6 +13,7 @@ import 'package:privacyidea_authenticator/state_notifiers/token_notifier.dart'; import 'package:privacyidea_authenticator/utils/app_customizer.dart'; import 'package:privacyidea_authenticator/utils/logger.dart'; import 'package:privacyidea_authenticator/utils/riverpod_providers.dart'; +import 'package:privacyidea_authenticator/utils/version.dart'; import 'package:privacyidea_authenticator/views/main_view/main_view.dart'; import 'package:privacyidea_authenticator/views/main_view/main_view_widgets/token_widgets/hotp_token_widgets/hotp_token_widget_tile.dart'; import 'package:privacyidea_authenticator/views/main_view/main_view_widgets/token_widgets/totp_token_widgets/totp_token_widget_tile.dart'; @@ -44,10 +47,11 @@ void main() { late final MockSettingsRepository mockSettingsRepository; late final MockTokenRepository mockTokenRepository; late final MockTokenFolderRepository mockTokenFolderRepository; + late final MockIntroductionRepository mockIntroductionRepository; setUp(() { mockSettingsRepository = MockSettingsRepository(); - when(mockSettingsRepository.loadSettings()) - .thenAnswer((_) async => SettingsState(isFirstRun: false, useSystemLocale: false, localePreference: const Locale('en'))); + when(mockSettingsRepository.loadSettings()).thenAnswer((_) async => + SettingsState(isFirstRun: false, useSystemLocale: false, localePreference: const Locale('en'), latestVersion: Version.parse('999.999.999'))); when(mockSettingsRepository.saveSettings(any)).thenAnswer((_) async => true); mockTokenRepository = MockTokenRepository(); when(mockTokenRepository.loadTokens()).thenAnswer((_) async => []); @@ -56,6 +60,9 @@ void main() { mockTokenFolderRepository = MockTokenFolderRepository(); when(mockTokenFolderRepository.loadFolders()).thenAnswer((_) async => []); when(mockTokenFolderRepository.saveOrReplaceFolders(any)).thenAnswer((_) async => []); + mockIntroductionRepository = MockIntroductionRepository(); + final introductions = {...Introduction.values}..remove(Introduction.introductionScreen); + when(mockIntroductionRepository.loadCompletedIntroductions()).thenAnswer((_) async => IntroductionState(completedIntroductions: introductions)); }); testWidgets( '2step rollout test', diff --git a/integration_test/views_test.dart b/integration_test/views_test.dart index fc0a1f433..7e189b26f 100644 --- a/integration_test/views_test.dart +++ b/integration_test/views_test.dart @@ -6,6 +6,8 @@ import 'package:mockito/mockito.dart'; import 'package:pointycastle/asymmetric/api.dart'; import 'package:privacyidea_authenticator/l10n/app_localizations_en.dart'; import 'package:privacyidea_authenticator/mains/main_netknights.dart'; +import 'package:privacyidea_authenticator/model/enums/introduction.dart'; +import 'package:privacyidea_authenticator/model/states/introduction_state.dart'; import 'package:privacyidea_authenticator/model/states/settings_state.dart'; import 'package:privacyidea_authenticator/state_notifiers/settings_notifier.dart'; import 'package:privacyidea_authenticator/state_notifiers/token_folder_notifier.dart'; @@ -13,6 +15,7 @@ import 'package:privacyidea_authenticator/state_notifiers/token_notifier.dart'; import 'package:privacyidea_authenticator/utils/app_customizer.dart'; import 'package:privacyidea_authenticator/utils/riverpod_providers.dart'; import 'package:privacyidea_authenticator/utils/rsa_utils.dart'; +import 'package:privacyidea_authenticator/utils/version.dart'; import 'package:privacyidea_authenticator/views/settings_view/settings_view_widgets/settings_groups.dart'; import '../test/tests_app_wrapper.dart'; @@ -26,10 +29,11 @@ void main() { late final MockRsaUtils mockRsaUtils; late final MockFirebaseUtils mockFirebaseUtils; late final MockPrivacyIdeaIOClient mockIOClient; + late final MockIntroductionRepository mockIntroductionRepository; setUp(() { mockSettingsRepository = MockSettingsRepository(); - when(mockSettingsRepository.loadSettings()) - .thenAnswer((_) async => SettingsState(isFirstRun: false, useSystemLocale: false, localePreference: const Locale('en'))); + when(mockSettingsRepository.loadSettings()).thenAnswer((_) async => + SettingsState(isFirstRun: false, useSystemLocale: false, localePreference: const Locale('en'), latestVersion: Version.parse('999.999.999'))); when(mockSettingsRepository.saveSettings(any)).thenAnswer((_) async => true); mockTokenRepository = MockTokenRepository(); when(mockTokenRepository.loadTokens()).thenAnswer((_) async => []); @@ -51,6 +55,9 @@ void main() { body: anyNamed('body'), sslVerify: anyNamed('sslVerify'), )).thenAnswer((_) => Future.value(Response('{"detail": {"public_key": "publicKey"}}', 200))); + mockIntroductionRepository = MockIntroductionRepository(); + final introductions = {...Introduction.values}..remove(Introduction.introductionScreen); + when(mockIntroductionRepository.loadCompletedIntroductions()).thenAnswer((_) async => IntroductionState(completedIntroductions: introductions)); }); testWidgets('Views Test', (tester) async { @@ -101,7 +108,7 @@ Future _settingsViewTest(WidgetTester tester) async { expect(find.text(AppLocalizationsEn().theme), findsOneWidget); expect(find.text(AppLocalizationsEn().language), findsOneWidget); expect(find.text(AppLocalizationsEn().errorLogTitle), findsOneWidget); - expect(find.byType(SettingsGroup), findsNWidgets(4)); + expect(find.byType(SettingsGroup), findsNWidgets(5)); globalRef!.read(tokenProvider.notifier).handleQrCode( 'otpauth://pipush/label?url=http%3A%2F%2Fwww.example.com&ttl=10&issuer=issuer&enrollment_credential=enrollmentCredentials&v=1&serial=serial&serial=serial&sslverify=0'); await pumpUntilFindNWidgets(tester, find.text(AppLocalizationsEn().pushToken), 1, const Duration(minutes: 5)); diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 000000000..4af9ef4a4 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,34 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* +**.last_build_id + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index d63440f43..8c6e56146 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -1,26 +1,26 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 11.0 - - + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index be1c34f85..52938b5ca 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-netknights/Pods-netknights.debug-netknights.xcconfig" -#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug-netknights.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/Flutter.podspec b/ios/Flutter/Flutter.podspec new file mode 100644 index 000000000..98e163395 --- /dev/null +++ b/ios/Flutter/Flutter.podspec @@ -0,0 +1,18 @@ +# +# This podspec is NOT to be published. It is only used as a local source! +# This is a generated file; do not edit or check into version control. +# + +Pod::Spec.new do |s| + s.name = 'Flutter' + s.version = '1.0.0' + s.summary = 'A UI toolkit for beautiful and fast apps.' + s.homepage = 'https://flutter.dev' + s.license = { :type => 'BSD' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } + s.ios.deployment_target = '12.0' + # Framework linking is handled by Flutter tooling, not CocoaPods. + # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. + s.vendored_frameworks = 'path/to/nothing' +end diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 4396dbffe..399e9340e 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-netknights/Pods-netknights.release-netknights.xcconfig" -#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh index 8d8c33c47..babd0ce4d 100755 --- a/ios/Flutter/flutter_export_environment.sh +++ b/ios/Flutter/flutter_export_environment.sh @@ -1,13 +1,15 @@ #!/bin/sh # This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=C:\src\flutter" -export "FLUTTER_APPLICATION_PATH=C:\Users\Frank Merkel\Documents\GitHub\pi-authenticator" +export "FLUTTER_ROOT=/Users/frankmerkel/src/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/frankmerkel/Documents/GitHub/pi-authenticator" export "COCOAPODS_PARALLEL_CODE_SIGN=true" -export "FLUTTER_TARGET=lib\main.dart" +export "FLUTTER_TARGET=/Users/frankmerkel/Documents/GitHub/pi-authenticator/lib/mains/main_netknights.dart" export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_NAME=4.3.0" -export "FLUTTER_BUILD_NUMBER=403002" +export "FLUTTER_BUILD_NUMBER=403006" +export "DART_DEFINES=RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==,RkxVVFRFUl9XRUJfQ0FOVkFTS0lUX1VSTD1odHRwczovL3d3dy5nc3RhdGljLmNvbS9mbHV0dGVyLWNhbnZhc2tpdC8wNDgxN2M5OWM5ZmQ0OTU2ZjI3NTA1MjA0ZjdlMzQ0MzM1ODEwYWVkLw==,RkxVVFRFUl9BUFBfRkxBVk9SPW5ldGtuaWdodHM=" export "DART_OBFUSCATION=false" export "TRACK_WIDGET_CREATION=true" export "TREE_SHAKE_ICONS=false" -export "PACKAGE_CONFIG=.dart_tool/package_config.json" +export "PACKAGE_CONFIG=/Users/frankmerkel/Documents/GitHub/pi-authenticator/.dart_tool/package_config.json" +export "FLAVOR=netknights" diff --git a/ios/Podfile b/ios/Podfile index 646a6a5cc..ad8656662 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,80 +1,79 @@ -# Uncomment this line to define a global platform for your project -platform :ios, '12.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug-netknights' => :debug, - 'Profile-netknights' => :release, - 'Release-netknights' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'netknights' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - - -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['ENABLE_BITCODE'] = 'NO' - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.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 +# Uncomment this line to define a global platform for your project +platform :ios, '12.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +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['ENABLE_BITCODE'] = 'NO' + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.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 df632744d..417a04414 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -7,18 +7,27 @@ objects = { /* Begin PBXBuildFile section */ + 00676387BBAD2ECE85BE2277 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E42A9F64AD58498FFAD5CBC9 /* Pods_Runner.framework */; }; 0DCCE7B526CE7AA30029E1D5 /* BuildFile in Resources */ = {isa = PBXBuildFile; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 367DB8C02AD8046200B964C1 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0DA36CBC26D4E8F400CDA64B /* GoogleService-Info.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 92746B5A38C07B4BC6C487E0 /* Pods_netknights.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 74D8E815BF59CA76F866F6AC /* Pods_netknights.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ + 369B8C942B459657000E0321 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -36,24 +45,29 @@ 0D32FBC626A84C0B0033DD09 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/LaunchScreen.strings; sourceTree = ""; }; 0D32FBC726A84E6D0033DD09 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Main.strings; sourceTree = ""; }; 0D32FBC826A84E6D0033DD09 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/LaunchScreen.strings; sourceTree = ""; }; - 0DA36CBC26D4E8F400CDA64B /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 154518E81C04D464E3B945E8 /* Pods-netknights.debug-netknights.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-netknights.debug-netknights.xcconfig"; path = "Target Support Files/Pods-netknights/Pods-netknights.debug-netknights.xcconfig"; sourceTree = ""; }; + 1E95FF8E348251B814F790E0 /* Pods-Runner.release-netknights.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-netknights.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-netknights.xcconfig"; sourceTree = ""; }; + 28D3D76C3C6C8E4C14CCE44A /* Pods-Runner.debug-netknights_debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-netknights_debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-netknights_debug.xcconfig"; sourceTree = ""; }; 29E3D04F2458553B00DFB52B /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + 369B8C852B459656000E0321 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + 369B8C872B459656000E0321 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 36AD5FD02B83589200FB5A82 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 36AD5FD12B8358A400FB5A82 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 5E465111418945F34F095894 /* Pods-netknights.release-netknights.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-netknights.release-netknights.xcconfig"; path = "Target Support Files/Pods-netknights/Pods-netknights.release-netknights.xcconfig"; sourceTree = ""; }; + 6249C2B9F653B35E8A754300 /* Pods-Runner.debug-netknights.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug-netknights.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug-netknights.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 74D8E815BF59CA76F866F6AC /* Pods_netknights.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_netknights.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* netknights.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = netknights.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146EE1CF9000F007C117D /* privacyIDEA Authenticator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "privacyIDEA Authenticator.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B96C75D06F8805BD67D05389 /* Pods-Runner.release-netknights_debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-netknights_debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-netknights_debug.xcconfig"; sourceTree = ""; }; + E42A9F64AD58498FFAD5CBC9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -61,37 +75,57 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 92746B5A38C07B4BC6C487E0 /* Pods_netknights.framework in Frameworks */, + 00676387BBAD2ECE85BE2277 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0B40CA0E5C8ABE7273EBF171 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 369B8C852B459656000E0321 /* WidgetKit.framework */, + 369B8C872B459656000E0321 /* SwiftUI.framework */, + E42A9F64AD58498FFAD5CBC9 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 319E74BAB265CB63977BE434 /* Pods */ = { isa = PBXGroup; children = ( - 154518E81C04D464E3B945E8 /* Pods-netknights.debug-netknights.xcconfig */, - 5E465111418945F34F095894 /* Pods-netknights.release-netknights.xcconfig */, + 6249C2B9F653B35E8A754300 /* Pods-Runner.debug-netknights.xcconfig */, + 28D3D76C3C6C8E4C14CCE44A /* Pods-Runner.debug-netknights_debug.xcconfig */, + 1E95FF8E348251B814F790E0 /* Pods-Runner.release-netknights.xcconfig */, + B96C75D06F8805BD67D05389 /* Pods-Runner.release-netknights_debug.xcconfig */, ); path = Pods; sourceTree = ""; }; - 362402502AD6F0CA00D4A700 /* netknights */ = { + 36AD5FCD2B8357FE00FB5A82 /* config */ = { isa = PBXGroup; children = ( - 0DA36CBC26D4E8F400CDA64B /* GoogleService-Info.plist */, - 97C147021CF9000F007C117D /* Info.plist */, + 36AD5FCF2B83582500FB5A82 /* netknights_debug */, + 36AD5FCE2B83581700FB5A82 /* netknights */, + ); + path = config; + sourceTree = ""; + }; + 36AD5FCE2B83581700FB5A82 /* netknights */ = { + isa = PBXGroup; + children = ( + 36AD5FD02B83589200FB5A82 /* GoogleService-Info.plist */, ); path = netknights; sourceTree = ""; }; - 362402522AD6F0F600D4A700 /* config */ = { + 36AD5FCF2B83582500FB5A82 /* netknights_debug */ = { isa = PBXGroup; children = ( - 362402502AD6F0CA00D4A700 /* netknights */, + 36AD5FD12B8358A400FB5A82 /* GoogleService-Info.plist */, ); - path = config; + path = netknights_debug; sourceTree = ""; }; 9740EEB11CF90186004384FC /* Flutter */ = { @@ -108,19 +142,19 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( - 362402522AD6F0F600D4A700 /* config */, + 36AD5FCD2B8357FE00FB5A82 /* config */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 319E74BAB265CB63977BE434 /* Pods */, - E314BB55A4F95EA8D0C9B2F4 /* Frameworks */, + 0B40CA0E5C8ABE7273EBF171 /* Frameworks */, ); sourceTree = ""; }; 97C146EF1CF9000F007C117D /* Products */ = { isa = PBXGroup; children = ( - 97C146EE1CF9000F007C117D /* netknights.app */, + 97C146EE1CF9000F007C117D /* privacyIDEA Authenticator.app */, ); name = Products; sourceTree = ""; @@ -132,6 +166,7 @@ 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, @@ -148,38 +183,31 @@ name = "Supporting Files"; sourceTree = ""; }; - E314BB55A4F95EA8D0C9B2F4 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 74D8E815BF59CA76F866F6AC /* Pods_netknights.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* netknights */ = { + 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "netknights" */; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 90AEB0B2A5CA0A00548276BD /* [CP] Check Pods Manifest.lock */, - 9740EEB61CF901F6004384FC /* flavor specific GoogleServices-Info.plist files to the default location */, + E2F1707EA0792D3B53A1F0F3 /* [CP] Check Pods Manifest.lock */, + 369B8C942B459657000E0321 /* Embed Foundation Extensions */, + 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, + 36AD5FCC2B83563A00FB5A82 /* Copy GoogleService-Info.plist */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 362402532AD6F13000D4A700 /* flavor specific GoogleService-Info.plist files to the default location */, - F1B8313245573B6B39D5BF96 /* [CP] Embed Pods Frameworks */, + 62514266C71F18497DCD51C9 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); - name = netknights; + name = Runner; productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* netknights.app */; + productReference = 97C146EE1CF9000F007C117D /* privacyIDEA Authenticator.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -189,12 +217,13 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1500; + LastSwiftMigration = ""; }; }; }; @@ -213,7 +242,7 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 97C146ED1CF9000F007C117D /* netknights */, + 97C146ED1CF9000F007C117D /* Runner */, ); }; /* End PBXProject section */ @@ -228,14 +257,13 @@ 0DCCE7B526CE7AA30029E1D5 /* BuildFile in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - 367DB8C02AD8046200B964C1 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 362402532AD6F13000D4A700 /* flavor specific GoogleService-Info.plist files to the default location */ = { + 36AD5FCC2B83563A00FB5A82 /* Copy GoogleService-Info.plist */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -244,14 +272,14 @@ ); inputPaths = ( ); - name = "flavor specific GoogleService-Info.plist files to the default location"; + name = "Copy GoogleService-Info.plist"; outputFileListPaths = ( ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "environment=\"default\"\n\n# Regex to extract the scheme name from the Build Configuration\n# We have named our Build Configurations as Debug-dev, Debug-prod etc.\n# Here, dev and prod are the scheme names. This kind of naming is required by Flutter for flavors to work.\n# We are using the $CONFIGURATION variable available in the XCode build environment to extract \n# the environment (or flavor)\n# For eg.\n# If CONFIGURATION=\"Debug-prod\", then environment will get set to \"prod\".\nif [[ $CONFIGURATION =~ -([^-]*)$ ]]; then\nenvironment=${BASH_REMATCH[1]}\nfi\n\necho $environment\n\n# Name and path of the resource we're copying\nGOOGLESERVICE_INFO_PLIST=GoogleService-Info.plist\nGOOGLESERVICE_INFO_FILE=${PROJECT_DIR}/config/${environment}/${GOOGLESERVICE_INFO_PLIST}\n\n# Make sure GoogleService-Info.plist exists\necho \"Looking for ${GOOGLESERVICE_INFO_PLIST} in ${GOOGLESERVICE_INFO_FILE}\"\nif [ ! -f $GOOGLESERVICE_INFO_FILE ]\nthen\necho \"No GoogleService-Info.plist found. Please ensure it's in the proper directory.\"\nexit 1\nfi\n\n# Get a reference to the destination location for the GoogleService-Info.plist\n# This is the default location where Firebase init code expects to find GoogleServices-Info.plist file\nPLIST_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app\necho \"Will copy ${GOOGLESERVICE_INFO_PLIST} to final destination: ${PLIST_DESTINATION}\"\n\n# Copy over the prod GoogleService-Info.plist for Release builds\ncp \"${GOOGLESERVICE_INFO_FILE}\" \"${PLIST_DESTINATION}\"\n"; + shellScript = "environment=\"default\"\n\n# Regex to extract the scheme name from the Build Configuration\n# We have named our Build Configurations as Debug-dev, Debug-prod etc.\n# Here, dev and prod are the scheme names. This kind of naming is required by Flutter for flavors to work.\n# We are using the $CONFIGURATION variable available in the XCode build environment to extract \n# the environment (or flavor)\n# For eg.\n# If CONFIGURATION=\"Debug-prod\", then environment will get set to \"prod\".\nif [[ $CONFIGURATION =~ -([^-]*)$ ]]; then\nenvironment=${BASH_REMATCH[1]}\nfi\n\necho $environment\n\n# Name and path of the resource we're copying\nGOOGLESERVICE_INFO_PLIST=GoogleService-Info.plist\nGOOGLESERVICE_INFO_FILE=${PROJECT_DIR}/config/${environment}/${GOOGLESERVICE_INFO_PLIST}\n\n# Make sure GoogleService-Info.plist exists\necho \"Looking for ${GOOGLESERVICE_INFO_PLIST} in ${GOOGLESERVICE_INFO_FILE}\"\nif [ ! -f $GOOGLESERVICE_INFO_FILE ]\nthen\necho \"No GoogleService-Info.plist found. Please ensure it's in the proper directory.\"\nexit 1\nfi\n\n# Get a reference to the destination location for the GoogleService-Info.plist\n# This is the default location where Firebase init code expects to find GoogleServices-Info.plist file\nPLIST_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app\necho \"Will copy ${GOOGLESERVICE_INFO_PLIST} to final destination: ${PLIST_DESTINATION}\"\n\n# Copy over the prod GoogleService-Info.plist for Release builds\ncp \"${GOOGLESERVICE_INFO_FILE}\" \"${PLIST_DESTINATION}\"\n\n"; }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; @@ -269,50 +297,13 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 90AEB0B2A5CA0A00548276BD /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-netknights-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 9740EEB61CF901F6004384FC /* flavor specific GoogleServices-Info.plist files to the default location */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "flavor specific GoogleServices-Info.plist files to the default location"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; - }; - F1B8313245573B6B39D5BF96 /* [CP] Embed Pods Frameworks */ = { + 62514266C71F18497DCD51C9 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-netknights/Pods-netknights-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", "${BUILT_PRODUCTS_DIR}/FirebaseInstallations/FirebaseInstallations.framework", @@ -375,7 +366,44 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-netknights/Pods-netknights-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; + }; + E2F1707EA0792D3B53A1F0F3 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -416,8 +444,9 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ - 3668D5292AD57ED90072556D /* Release-netknights */ = { + 36AD5FC62B83483100FB5A82 /* Debug-netknights */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB31CF90195004384FC /* Generated.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; @@ -440,6 +469,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -447,11 +477,17 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; + DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -459,36 +495,30 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = NO; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; }; - name = "Release-netknights"; + name = "Debug-netknights"; }; - 3668D52A2AD57ED90072556D /* Release-netknights */ = { + 36AD5FC72B83483100FB5A82 /* Debug-netknights */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + baseConfigurationReference = 6249C2B9F653B35E8A754300 /* Pods-Runner.debug-netknights.xcconfig */; buildSettings = { - APP_DISPLAY_NAME = "privacyIDEA Authenticator"; ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-netknights"; - ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 627QALYL3B; ENABLE_BITCODE = NO; - FLUTTER_APPLICATION_PATH = "/Users/frankmerkel/Documents/GitHub/pi-authenticator"; - FLUTTER_BUILD_DIR = build; - FLUTTER_BUILD_NUMBER = "$(FLUTTER_BUILD_NUMBER)"; - FLUTTER_TARGET = "$(FLUTTER_APPLICATION_PATH)/lib/mains/main_netknights.dart"; + FLUTTER_TARGET = lib/mains/main_netknights.dart; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - INFOPLIST_FILE = config/netknights/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "privacyIDEA authenticator"; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "privacyIDEA Authenticator"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -501,15 +531,17 @@ ); MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; PRODUCT_BUNDLE_IDENTIFIER = privacyidea.authenticator; - PRODUCT_NAME = netknights; + PRODUCT_NAME = "privacyIDEA Authenticator"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; }; - name = "Release-netknights"; + name = "Debug-netknights"; }; - 36DA56942AD5648700023F93 /* Debug-netknights */ = { + 36AD5FC82B834F2A00FB5A82 /* Debug-netknights_debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB31CF90195004384FC /* Generated.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; @@ -532,6 +564,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -562,30 +595,206 @@ SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; }; - name = "Debug-netknights"; + name = "Debug-netknights_debug"; + }; + 36AD5FC92B834F2A00FB5A82 /* Debug-netknights_debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 28D3D76C3C6C8E4C14CCE44A /* Pods-Runner.debug-netknights_debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-netknights_debug"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 627QALYL3B; + ENABLE_BITCODE = NO; + FLUTTER_TARGET = lib/mains/main_netknights.dart; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "internal - privacyIDEA Authenticator"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; + PRODUCT_BUNDLE_IDENTIFIER = privacyidea.authenticator.debug; + PRODUCT_NAME = "internal - privacyIDEA Authenticator"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = "Debug-netknights_debug"; + }; + 36AD5FCA2B834F4600FB5A82 /* Release-netknights_debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB31CF90195004384FC /* Generated.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = "Release-netknights_debug"; }; - 36DA56952AD5648700023F93 /* Debug-netknights */ = { + 36AD5FCB2B834F4600FB5A82 /* Release-netknights_debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + baseConfigurationReference = B96C75D06F8805BD67D05389 /* Pods-Runner.release-netknights_debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-netknights_debug"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 627QALYL3B; + ENABLE_BITCODE = NO; + FLUTTER_TARGET = lib/mains/main_netknights.dart; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "internal - privacyIDEA Authenticator"; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; + PRODUCT_BUNDLE_IDENTIFIER = privacyidea.authenticator.debug; + PRODUCT_NAME = "internal - privacyIDEA Authenticator"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 4.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = "Release-netknights_debug"; + }; + 97C147041CF9000F007C117D /* Release-netknights */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB31CF90195004384FC /* Generated.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = "Release-netknights"; + }; + 97C147071CF9000F007C117D /* Release-netknights */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1E95FF8E348251B814F790E0 /* Pods-Runner.release-netknights.xcconfig */; buildSettings = { - APP_DISPLAY_NAME = "(debug) privacyIDEA Authenticator"; ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-netknights"; - ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = 627QALYL3B; ENABLE_BITCODE = NO; - FLUTTER_APPLICATION_PATH = "/Users/frankmerkel/Documents/GitHub/pi-authenticator"; - FLUTTER_BUILD_DIR = build; - FLUTTER_BUILD_NUMBER = "$(FLUTTER_BUILD_NUMBER)"; - FLUTTER_TARGET = "$(FLUTTER_APPLICATION_PATH)/lib/mains/main_netknights.dart"; + FLUTTER_TARGET = lib/mains/main_netknights.dart; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - INFOPLIST_FILE = config/netknights/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "(debug) privacyIDEA authenticator"; + INFOPLIST_FILE = Runner/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = "privacyIDEA Authenticator"; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -598,13 +807,12 @@ ); MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)"; PRODUCT_BUNDLE_IDENTIFIER = privacyidea.authenticator; - PRODUCT_NAME = netknights; + PRODUCT_NAME = "privacyIDEA Authenticator"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; }; - name = "Debug-netknights"; + name = "Release-netknights"; }; /* End XCBuildConfiguration section */ @@ -612,20 +820,24 @@ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( - 36DA56942AD5648700023F93 /* Debug-netknights */, - 3668D5292AD57ED90072556D /* Release-netknights */, + 36AD5FC62B83483100FB5A82 /* Debug-netknights */, + 36AD5FC82B834F2A00FB5A82 /* Debug-netknights_debug */, + 97C147041CF9000F007C117D /* Release-netknights */, + 36AD5FCA2B834F4600FB5A82 /* Release-netknights_debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = "Debug-netknights"; + defaultConfigurationName = "Release-netknights"; }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "netknights" */ = { + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( - 36DA56952AD5648700023F93 /* Debug-netknights */, - 3668D52A2AD57ED90072556D /* Release-netknights */, + 36AD5FC72B83483100FB5A82 /* Debug-netknights */, + 36AD5FC92B834F2A00FB5A82 /* Debug-netknights_debug */, + 97C147071CF9000F007C117D /* Release-netknights */, + 36AD5FCB2B834F4600FB5A82 /* Release-netknights_debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = "Debug-netknights"; + defaultConfigurationName = "Release-netknights"; }; /* End XCConfigurationList section */ }; diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index c4b79bd8c..919434a62 100644 --- a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -1,7 +1,7 @@ - - - - - + + + + + diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist index fc6bf8074..18d981003 100644 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -1,8 +1,8 @@ - - - - - IDEDidComputeMac32BitWarning - - - + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/netknights.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/netknights.xcscheme index fee20ee15..1f827ffa1 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/netknights.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/netknights.xcscheme @@ -1,87 +1,77 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/netknights_debug.xcscheme similarity index 56% rename from ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to ios/Runner.xcodeproj/xcshareddata/xcschemes/netknights_debug.xcscheme index 0413f4637..a129934d1 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/netknights_debug.xcscheme @@ -1,46 +1,36 @@ + LastUpgradeVersion = "1510" + version = "1.7"> + buildForAnalyzing = "NO"> - - - - - - + shouldUseLaunchSchemeArgsEnv = "YES" + shouldAutocreateTestPlan = "YES"> - - - - + buildConfiguration = "Debug-netknights_debug"> diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 17ccc03e6..21a3cc14c 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -1,10 +1,10 @@ - - - - - - - + + + + + + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist index fc6bf8074..18d981003 100644 --- a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -1,8 +1,8 @@ - - - - - IDEDidComputeMac32BitWarning - - - + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 8e3b337e8..3ade2c439 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,16 +1,16 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - if #available(iOS 10.0, *) { - UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate - } - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + if #available(iOS 10.0, *) { + UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate + } + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json index d08a4de32..0bedcf2fd 100644 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -1,23 +1,23 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md index 65a94b5db..89c2725b7 100644 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -1,5 +1,5 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard index 497371ea2..f2e259c7c 100644 --- a/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ b/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -1,37 +1,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard index bbb83caae..f3c28516f 100644 --- a/ios/Runner/Base.lproj/Main.storyboard +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -1,26 +1,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 5aa3e91fa..bd0efa95a 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -1,86 +1,86 @@ - - - - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLocalizations - - en - de - - CFBundleName - privacyidea.authenticator - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - it.netknights.otpauth - CFBundleURLSchemes - - otpauth - - - - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - FIREBASE_ANALYTICS_COLLECTION_DISABLED - - FirebaseMessagingAutoInitEnabled - - 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 - remote-notification - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortrait - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UIViewControllerBasedStatusBarAppearance - - - + + + + + CFBundleName + $(BUNDLE_DISPLAY_NAME) + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLocalizations + + en + de + + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + it.netknights.otpauth + CFBundleURLSchemes + + otpauth + + + + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + FIREBASE_ANALYTICS_COLLECTION_DISABLED + + FirebaseMessagingAutoInitEnabled + + 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 + remote-notification + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements index 8d158517a..91f8eb453 100644 --- a/ios/Runner/Runner.entitlements +++ b/ios/Runner/Runner.entitlements @@ -1,8 +1,12 @@ - - - - - aps-environment - development - - + + + + + aps-environment + development + com.apple.security.application-groups + + group.authenticator_home_widget_group + + + diff --git a/ios/Runner/de.lproj/LaunchScreen.strings b/ios/Runner/de.lproj/LaunchScreen.strings index d3f5a12fa..8b1378917 100644 --- a/ios/Runner/de.lproj/LaunchScreen.strings +++ b/ios/Runner/de.lproj/LaunchScreen.strings @@ -1 +1 @@ - + diff --git a/ios/Runner/de.lproj/Main.strings b/ios/Runner/de.lproj/Main.strings index d3f5a12fa..8b1378917 100644 --- a/ios/Runner/de.lproj/Main.strings +++ b/ios/Runner/de.lproj/Main.strings @@ -1 +1 @@ - + diff --git a/ios/Runner/fr.lproj/LaunchScreen.strings b/ios/Runner/fr.lproj/LaunchScreen.strings index d3f5a12fa..8b1378917 100644 --- a/ios/Runner/fr.lproj/LaunchScreen.strings +++ b/ios/Runner/fr.lproj/LaunchScreen.strings @@ -1 +1 @@ - + diff --git a/ios/Runner/fr.lproj/Main.strings b/ios/Runner/fr.lproj/Main.strings index d3f5a12fa..8b1378917 100644 --- a/ios/Runner/fr.lproj/Main.strings +++ b/ios/Runner/fr.lproj/Main.strings @@ -1 +1 @@ - + diff --git a/ios/config/netknights/GoogleService-Info.plist b/ios/config/netknights/GoogleService-Info.plist index 6276b64e3..f5073c28f 100644 --- a/ios/config/netknights/GoogleService-Info.plist +++ b/ios/config/netknights/GoogleService-Info.plist @@ -1,34 +1,34 @@ - - - - - CLIENT_ID - 838329625695-2atauche4u5p406p0pklghk2fu25jago.apps.googleusercontent.com - REVERSED_CLIENT_ID - com.googleusercontent.apps.838329625695-2atauche4u5p406p0pklghk2fu25jago - API_KEY - AIzaSyAwOd_C2jPeHTCgw74VBTqm8zMr2BOWCeg - GCM_SENDER_ID - 838329625695 - PLIST_VERSION - 1 - BUNDLE_ID - privacyidea.authenticator - PROJECT_ID - privacyidea-4c8c1 - STORAGE_BUCKET - privacyidea-4c8c1.appspot.com - IS_ADS_ENABLED - - IS_ANALYTICS_ENABLED - - IS_APPINVITE_ENABLED - - IS_GCM_ENABLED - - IS_SIGNIN_ENABLED - - GOOGLE_APP_ID - 1:838329625695:ios:9926651df24088af243fea - - \ No newline at end of file + + + + + CLIENT_ID + 838329625695-2atauche4u5p406p0pklghk2fu25jago.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.838329625695-2atauche4u5p406p0pklghk2fu25jago + API_KEY + AIzaSyAwOd_C2jPeHTCgw74VBTqm8zMr2BOWCeg + GCM_SENDER_ID + 838329625695 + PLIST_VERSION + 1 + BUNDLE_ID + privacyidea.authenticator + PROJECT_ID + privacyidea-4c8c1 + STORAGE_BUCKET + privacyidea-4c8c1.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:838329625695:ios:9926651df24088af243fea + + diff --git a/ios/config/netknights/Info.plist b/ios/config/netknights/Info.plist deleted file mode 100644 index ff7d410f7..000000000 --- a/ios/config/netknights/Info.plist +++ /dev/null @@ -1,90 +0,0 @@ - - - - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - $(APP_DISPLAY_NAME) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLocalizations - - en - de - - CFBundleName - privacyIDEA Authenticator - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - it.netknights.otpauth - CFBundleURLSchemes - - otpauth - - - - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - FIREBASE_ANALYTICS_COLLECTION_DISABLED - - FirebaseMessagingAutoInitEnabled - - 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 - remote-notification - - UILaunchStoryboardName - LaunchScreen.storyboard - UIMainStoryboardFile - Main - UIRequiresFullScreen - - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortrait - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/ios/config/netknights_debug/GoogleService-Info.plist b/ios/config/netknights_debug/GoogleService-Info.plist new file mode 100644 index 000000000..24434e1f5 --- /dev/null +++ b/ios/config/netknights_debug/GoogleService-Info.plist @@ -0,0 +1,34 @@ + + + + + CLIENT_ID + 838329625695-2atauche4u5p406p0pklghk2fu25jago.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.838329625695-2atauche4u5p406p0pklghk2fu25jago + API_KEY + AIzaSyAwOd_C2jPeHTCgw74VBTqm8zMr2BOWCeg + GCM_SENDER_ID + 838329625695 + PLIST_VERSION + 1 + BUNDLE_ID + privacyidea.authenticator.debug + PROJECT_ID + privacyidea-4c8c1 + STORAGE_BUCKET + privacyidea-4c8c1.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:838329625695:ios:9926651df24088af243fea + + diff --git a/lib/interfaces/repo/push_request_repository.dart b/lib/interfaces/repo/push_request_repository.dart new file mode 100644 index 000000000..040bf906d --- /dev/null +++ b/lib/interfaces/repo/push_request_repository.dart @@ -0,0 +1,7 @@ +import '../../model/push_request.dart'; + +abstract class PushRequestRepository { + Future saveOrReplacePushRequests(List pushRequests); + Future> loadPushRequests(); + Future deletePushRequest(PushRequest pushRequest); +} diff --git a/lib/interfaces/repo/token_repository.dart b/lib/interfaces/repo/token_repository.dart index 40951a61b..c15d34211 100644 --- a/lib/interfaces/repo/token_repository.dart +++ b/lib/interfaces/repo/token_repository.dart @@ -3,5 +3,7 @@ import '../../model/tokens/token.dart'; abstract class TokenRepository { Future> saveOrReplaceTokens(List tokens); Future> loadTokens(); + + //Returns the tokens that were not deleted Future> deleteTokens(List tokens); } diff --git a/lib/l10n/app_cs.arb b/lib/l10n/app_cs.arb index 16184737e..849fe434c 100644 --- a/lib/l10n/app_cs.arb +++ b/lib/l10n/app_cs.arb @@ -559,5 +559,19 @@ "feedbackSentTitle": "Zpětná vazba odeslána", "feedbackSentDescription": "Děkujeme vám za pomoc při vylepšování této aplikace!", "patchNotesDialogTitle": "Co je nového?", - "version": "Verze" + "version": "Verze", + "noMailAppTitle": "Není nainstalována žádná e-mailová aplikace", + "noMailAppDescription": "There is no e-mail app installed or initialised on this device, please try again when you are able to send an email message.", + "authenticationRequest": "Žádost o ověření", + "requestInfo": "Odesláno {issuer} pro váš účet: \"{account}\"", + "@requestInfo": { + "placeholders": { + "issuer": { + "example": "privacyIDEA" + }, + "account": { + "example": "GitHub" + } + } + } } \ No newline at end of file diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 1302c5b1a..e70ba1c33 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -538,5 +538,19 @@ "feedbackSentTitle": "Feedback gesendet", "feedbackSentDescription": "Vielen Dank für Ihre Hilfe bei der Verbesserung dieser App!", "patchNotesDialogTitle": "Was ist neu?", - "version": "Version" + "version": "Version", + "noMailAppTitle": "Keine Mail-App gefunden", + "noMailAppDescription": "Auf diesem Gerät ist keine E-Mail-App installiert oder initialisiert, bitte versuchen Sie es erneut, wenn Sie eine E-Mail-Nachricht senden können.", + "authenticationRequest": "Authentifizierung", + "requestInfo": "Gesendet von {issuer} für Ihr Konto: \"{account}\"", + "@requestInfo": { + "placeholders": { + "issuer": { + "example": "privacyIDEA" + }, + "Konto": { + "example": "GitHub" + } + } + } } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 0b334eb17..b8be4e2dd 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -549,5 +549,20 @@ "feedbackSentTitle": "Feedback sent", "feedbackSentDescription": "Thank you very much for your help in making this application better!", "patchNotesDialogTitle": "What's new?", - "version": "Version" + "version": "Version", + "noMailAppTitle": "No mail app found", + "noMailAppDescription": "There is no e-mail app installed or initialised on this device, please try again when you are able to send an email message.", + "authenticationRequest": "Authentication request", + "requestInfo": "Sent by {issuer} for your account: \"{account}\"", + "@requestInfo": { + "description": "Description of the authentication request.", + "placeholders": { + "issuer": { + "example": "privacyIDEA" + }, + "account": { + "example": "GitHub" + } + } + } } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index bf45e5b8d..282f949ea 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -555,5 +555,19 @@ "feedbackSentTitle": "Comentarios enviados", "feedbackSentDescription": "Muchas gracias por su ayuda para mejorar esta aplicación.", "patchNotesDialogTitle": "¿Qué hay de nuevo?", - "version": "Versión" + "version": "Versión", + "noMailAppTitle": "No hay aplicación de correo electrónico", + "noMailAppDescription": "No hay ninguna app de correo electrónico instalada o inicializada en este dispositivo, inténtalo de nuevo cuando puedas enviar un mensaje de correo electrónico.", + "authenticationRequest": "Autenticación", + "requestInfo": "Enviado por {issuer} para su cuenta: \"{account}\"", + "@requestInfo": { + "placeholders": { + "issuer": { + "example": "privacyIDEA" + }, + "account": { + "example": "GitHub" + } + } + } } \ No newline at end of file diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 428cf31c7..d15cd3053 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -560,5 +560,19 @@ "feedbackSentTitle": "Retour d'information envoyé", "feedbackSentDescription": "Merci beaucoup pour votre aide dans l'amélioration de cette application !", "patchNotesDialogTitle": "Quoi de neuf ?", - "version": "Version" + "version": "Version", + "noMailAppTitle": "Aucune application de messagerie trouvée", + "noMailAppDescription": "Aucune application de messagerie n'est installée ou initialisée sur cet appareil. Veuillez réessayer lorsque vous serez en mesure d'envoyer un message électronique.", + "authenticationRequest": "Authentification", + "requestInfo": "Envoyé par {issuer} pour votre compte : \"{account}\"", + "@requestInfo": { + "placeholders": { + "issuer": { + "exemple": "privacyIDEA" + }, + "account": { + "exemple": "GitHub" + } + } + } } \ No newline at end of file diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index ce0198c9c..b9db77f7e 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1368,6 +1368,30 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Version'** String get version; + + /// No description provided for @noMailAppTitle. + /// + /// In en, this message translates to: + /// **'No mail app found'** + String get noMailAppTitle; + + /// No description provided for @noMailAppDescription. + /// + /// In en, this message translates to: + /// **'There is no e-mail app installed or initialised on this device, please try again when you are able to send an email message.'** + String get noMailAppDescription; + + /// No description provided for @authenticationRequest. + /// + /// In en, this message translates to: + /// **'Authentication request'** + String get authenticationRequest; + + /// Description of the authentication request. + /// + /// In en, this message translates to: + /// **'Sent by {issuer} for your account: \"{account}\"'** + String requestInfo(Object issuer, Object account); } class _AppLocalizationsDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/app_localizations_cs.dart b/lib/l10n/app_localizations_cs.dart index ef9869969..301284cff 100644 --- a/lib/l10n/app_localizations_cs.dart +++ b/lib/l10n/app_localizations_cs.dart @@ -695,4 +695,18 @@ class AppLocalizationsCs extends AppLocalizations { @override String get version => 'Verze'; + + @override + String get noMailAppTitle => 'Není nainstalována žádná e-mailová aplikace'; + + @override + String get noMailAppDescription => 'There is no e-mail app installed or initialised on this device, please try again when you are able to send an email message.'; + + @override + String get authenticationRequest => 'Žádost o ověření'; + + @override + String requestInfo(Object issuer, Object account) { + return 'Odesláno $issuer pro váš účet: \"$account\"'; + } } diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 3ad3b8afd..41e21965b 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -695,4 +695,18 @@ class AppLocalizationsDe extends AppLocalizations { @override String get version => 'Version'; + + @override + String get noMailAppTitle => 'Keine Mail-App gefunden'; + + @override + String get noMailAppDescription => 'Auf diesem Gerät ist keine E-Mail-App installiert oder initialisiert, bitte versuchen Sie es erneut, wenn Sie eine E-Mail-Nachricht senden können.'; + + @override + String get authenticationRequest => 'Authentifizierung'; + + @override + String requestInfo(Object issuer, Object account) { + return 'Gesendet von $issuer für Ihr Konto: \"$account\"'; + } } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index c93fff38b..3d4c40998 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -695,4 +695,18 @@ class AppLocalizationsEn extends AppLocalizations { @override String get version => 'Version'; + + @override + String get noMailAppTitle => 'No mail app found'; + + @override + String get noMailAppDescription => 'There is no e-mail app installed or initialised on this device, please try again when you are able to send an email message.'; + + @override + String get authenticationRequest => 'Authentication request'; + + @override + String requestInfo(Object issuer, Object account) { + return 'Sent by $issuer for your account: \"$account\"'; + } } diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 7f737bd5e..fe1b6295b 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -695,4 +695,18 @@ class AppLocalizationsEs extends AppLocalizations { @override String get version => 'Versión'; + + @override + String get noMailAppTitle => 'No hay aplicación de correo electrónico'; + + @override + String get noMailAppDescription => 'No hay ninguna app de correo electrónico instalada o inicializada en este dispositivo, inténtalo de nuevo cuando puedas enviar un mensaje de correo electrónico.'; + + @override + String get authenticationRequest => 'Autenticación'; + + @override + String requestInfo(Object issuer, Object account) { + return 'Enviado por $issuer para su cuenta: \"$account\"'; + } } diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index c3bdb0397..b7ff37c6d 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -695,4 +695,18 @@ class AppLocalizationsFr extends AppLocalizations { @override String get version => 'Version'; + + @override + String get noMailAppTitle => 'Aucune application de messagerie trouvée'; + + @override + String get noMailAppDescription => 'Aucune application de messagerie n\'est installée ou initialisée sur cet appareil. Veuillez réessayer lorsque vous serez en mesure d\'envoyer un message électronique.'; + + @override + String get authenticationRequest => 'Authentification'; + + @override + String requestInfo(Object issuer, Object account) { + return 'Envoyé par $issuer pour votre compte : \"$account\"'; + } } diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index ab103b617..9aad45cba 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -695,4 +695,18 @@ class AppLocalizationsNl extends AppLocalizations { @override String get version => 'Versie'; + + @override + String get noMailAppTitle => 'Geen mail app gevonden'; + + @override + String get noMailAppDescription => 'Er is geen e-mail app geïnstalleerd of geïnitialiseerd op dit apparaat, probeer het opnieuw wanneer u in staat bent om een e-mailbericht te verzenden.'; + + @override + String get authenticationRequest => 'Verificatieverzoek'; + + @override + String requestInfo(Object issuer, Object account) { + return 'Verzonden door $issuer voor uw account: \"$account\"'; + } } diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 80301abbc..4b64bc218 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -695,4 +695,18 @@ class AppLocalizationsPl extends AppLocalizations { @override String get version => 'Wersja'; + + @override + String get noMailAppTitle => 'Nie znaleziono aplikacji pocztowej'; + + @override + String get noMailAppDescription => 'Na tym urządzeniu nie zainstalowano ani nie zainicjowano aplikacji poczty e-mail, spróbuj ponownie, gdy będziesz w stanie wysłać wiadomość e-mail'; + + @override + String get authenticationRequest => 'Żądanie uwierzytelnienia'; + + @override + String requestInfo(Object issuer, Object account) { + return 'Wysłane przez $issuer dla twojego konta: \"$account\"'; + } } diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 0ae52d8f3..b2497e848 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -556,5 +556,19 @@ "feedbackSentTitle": "Feedback verzonden", "feedbackSentDescription": "Hartelijk dank voor je hulp om deze applicatie beter te maken!", "patchNotesDialogTitle": "Wat is er nieuw?", - "version": "Versie" + "version": "Versie", + "noMailAppTitle": "Geen mail app gevonden", + "noMailAppDescription": "Er is geen e-mail app geïnstalleerd of geïnitialiseerd op dit apparaat, probeer het opnieuw wanneer u in staat bent om een e-mailbericht te verzenden.", + "authenticationRequest": "Verificatieverzoek", + "requestInfo": "Verzonden door {issuer} voor uw account: \"{account}\"", + "@requestInfo": { + "placeholders": { + "issuer": { + "example": "privacyIDEA" + }, + "account": { + "example": "GitHub" + } + } + } } \ No newline at end of file diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 2a3608a1c..59fa86e45 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -553,5 +553,19 @@ "feedbackSentTitle": "Opinia została wysłana", "feedbackSentDescription": "Dziękujemy bardzo za pomoc w ulepszeniu tej aplikacji!", "patchNotesDialogTitle": "Co nowego?", - "version": "Wersja" + "version": "Wersja", + "noMailAppTitle": "Nie znaleziono aplikacji pocztowej", + "noMailAppDescription": "Na tym urządzeniu nie zainstalowano ani nie zainicjowano aplikacji poczty e-mail, spróbuj ponownie, gdy będziesz w stanie wysłać wiadomość e-mail", + "authenticationRequest": "Żądanie uwierzytelnienia", + "requestInfo": "Wysłane przez {issuer} dla twojego konta: \"{account}\"", + "@requestInfo": { + "placeholders": { + "issuer": { + "example": "privacyIDEA" + }, + "account": { + "example": "GitHub" + } + } + } } \ No newline at end of file diff --git a/lib/mains/main_netknights.dart b/lib/mains/main_netknights.dart index 2d1e0ae7a..658c4661b 100644 --- a/lib/mains/main_netknights.dart +++ b/lib/mains/main_netknights.dart @@ -21,7 +21,6 @@ import 'package:easy_dynamic_theme/easy_dynamic_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:home_widget/home_widget.dart'; import 'package:privacyidea_authenticator/l10n/app_localizations.dart'; import 'package:privacyidea_authenticator/utils/app_customizer.dart'; import 'package:privacyidea_authenticator/utils/globals.dart'; @@ -46,10 +45,8 @@ void main() async { navigatorKey: globalNavigatorKey, appRunner: () async { WidgetsFlutterBinding.ensureInitialized(); - await HomeWidget.setAppGroupId(appGroupId); - if (await HomeWidgetUtils.isHomeWidgetSupported) { - await HomeWidget.registerInteractivityCallback(homeWidgetBackgroundCallback); - } + await HomeWidgetUtils().registerInteractivityCallback(homeWidgetBackgroundCallback); + await HomeWidgetUtils().setAppGroupId(appGroupId); runApp(AppWrapper(child: PrivacyIDEAAuthenticator(customization: ApplicationCustomization.defaultCustomization))); }); } diff --git a/lib/model/states/introduction_state.dart b/lib/model/states/introduction_state.dart index d001786eb..794295efb 100644 --- a/lib/model/states/introduction_state.dart +++ b/lib/model/states/introduction_state.dart @@ -31,4 +31,7 @@ class IntroductionState { } bool isConditionFulfilled(WidgetRef ref, Introduction introduction) => introduction.isConditionFulfilled(ref, this); + + @override + String toString() => 'IntroductionState{completedIntroductions: $completedIntroductions}'; } diff --git a/lib/model/states/token_state.dart b/lib/model/states/token_state.dart index 672a3610c..2295f7a74 100644 --- a/lib/model/states/token_state.dart +++ b/lib/model/states/token_state.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import '../../utils/logger.dart'; import '../enums/push_token_rollout_state.dart'; import '../token_folder.dart'; -import '../tokens/hotp_token.dart'; import '../tokens/otp_token.dart'; import '../tokens/push_token.dart'; import '../tokens/token.dart'; @@ -12,13 +11,11 @@ import '../tokens/token.dart'; @immutable class TokenState { final List tokens; + final List lastlyUpdatedTokens; List get otpTokens => tokens.whereType().toList(); bool get hasOTPTokens => otpTokens.isNotEmpty; - List get hotpTokens => tokens.whereType().toList(); - bool get hasHOTPTokens => hotpTokens.isNotEmpty; - List get pushTokens => tokens.whereType().toList(); bool get hasPushTokens => pushTokens.isNotEmpty; bool get hasRolledOutPushTokens => pushTokens.any((element) => element.isRolledOut); @@ -26,7 +23,9 @@ class TokenState { List get pushTokensToRollOut => pushTokens.where((element) => !element.isRolledOut && element.rolloutState == PushTokenRollOutState.rolloutNotStarted).toList(); - TokenState({List tokens = const []}) : tokens = List.from(tokens) { + TokenState({List tokens = const [], List? lastlyUpdatedTokens}) + : tokens = List.from(tokens), + lastlyUpdatedTokens = lastlyUpdatedTokens ?? List.from(tokens) { _sort(this.tokens); } TokenState repaceList({List? tokens}) => TokenState(tokens: tokens ?? this.tokens); @@ -61,25 +60,27 @@ class TokenState { TokenState withToken(Token token) { final newTokens = List.from(tokens); newTokens.add(token); - return TokenState(tokens: newTokens); + return TokenState(tokens: newTokens, lastlyUpdatedTokens: [token]); } TokenState withTokens(List tokens) { final newTokens = List.from(this.tokens); newTokens.addAll(tokens); - return TokenState(tokens: newTokens); + return TokenState(tokens: newTokens, lastlyUpdatedTokens: tokens); } + // Removes the token from the State + // Sets the lastlyUpdatedTokens to an empty list because no token was updated only removed TokenState withoutToken(Token token) { final newTokens = List.from(tokens); newTokens.removeWhere((element) => element.id == token.id); - return TokenState(tokens: newTokens); + return TokenState(tokens: newTokens, lastlyUpdatedTokens: const []); } TokenState withoutTokens(List tokens) { final newTokens = List.from(this.tokens); newTokens.removeWhere((element) => tokens.any((token) => token.id == element.id)); - return TokenState(tokens: newTokens); + return TokenState(tokens: newTokens, lastlyUpdatedTokens: tokens); } // Add a token if it does not exist yet @@ -92,7 +93,7 @@ class TokenState { } else { newTokens[index] = token; } - return TokenState(tokens: newTokens); + return TokenState(tokens: newTokens, lastlyUpdatedTokens: [token]); } // Replace the token if it does exist @@ -105,7 +106,7 @@ class TokenState { return this; } newTokens[index] = token; - return TokenState(tokens: newTokens); + return TokenState(tokens: newTokens, lastlyUpdatedTokens: [token]); } // replace all tokens where the id is the same @@ -120,13 +121,14 @@ class TokenState { } newTokens[index] = token; } - return TokenState(tokens: newTokens); + return TokenState(tokens: newTokens, lastlyUpdatedTokens: tokens); } // Replace the tokens if it does exist // Do nothing if it does not exist TokenState replaceTokens(List tokens) { final newTokens = List.from(this.tokens); + final lastlyUpdatedTokens = []; for (var token in tokens) { final index = newTokens.indexWhere((element) => element.id == token.id); if (index == -1) { @@ -134,8 +136,9 @@ class TokenState { continue; } newTokens[index] = token; + lastlyUpdatedTokens.add(token); } - return TokenState(tokens: newTokens); + return TokenState(tokens: newTokens, lastlyUpdatedTokens: lastlyUpdatedTokens); } List tokensInFolder(TokenFolder folder, {List? only, List? exclude}) => tokens.where((token) { diff --git a/lib/model/tokens/totp_token.dart b/lib/model/tokens/totp_token.dart index e649ab831..aefee214a 100644 --- a/lib/model/tokens/totp_token.dart +++ b/lib/model/tokens/totp_token.dart @@ -107,7 +107,8 @@ class TOTPToken extends OTPToken { factory TOTPToken.fromUriMap(Map uriMap) { if (uriMap[URI_SECRET] == null) throw ArgumentError('Secret is required'); - if (uriMap[URI_DIGITS] < 1) throw ArgumentError('Digits must be greater than 0'); + if (uriMap[URI_DIGITS] != null && uriMap[URI_DIGITS] < 1) throw ArgumentError('Digits must be greater than 0'); + if (uriMap[URI_PERIOD] != null && uriMap[URI_PERIOD] < 1) throw ArgumentError('Period must be greater than 0'); TOTPToken totpToken; try { totpToken = TOTPToken( diff --git a/lib/processors/scheme_processors/navigation_scheme_processors/home_widget_navigate_processor.dart b/lib/processors/scheme_processors/navigation_scheme_processors/home_widget_navigate_processor.dart index e99656b7c..d26a4da46 100644 --- a/lib/processors/scheme_processors/navigation_scheme_processors/home_widget_navigate_processor.dart +++ b/lib/processors/scheme_processors/navigation_scheme_processors/home_widget_navigate_processor.dart @@ -5,7 +5,6 @@ import '../../../utils/home_widget_utils.dart'; import '../../../utils/logger.dart'; import '../../../utils/riverpod_providers.dart'; import '../../../views/link_home_widget_view/link_home_widget_view.dart'; -import '../../../views/main_view/main_view.dart'; import '../../../views/splash_screen/splash_screen.dart'; import 'navigation_scheme_processor_interface.dart'; @@ -45,7 +44,7 @@ class HomeWidgetNavigateProcessor implements NavigationSchemeProcessor { LinkHomeWidgetView(homeWidgetId: uri.queryParameters['id']!), ); } else { - Navigator.popUntil(context, (route) => route.settings.name == MainView.routeName); + Navigator.popUntil(context, (route) => route.isFirst); Navigator.of(context).push( MaterialPageRoute( builder: (context) => LinkHomeWidgetView(homeWidgetId: uri.queryParameters['id']!), @@ -64,18 +63,25 @@ class HomeWidgetNavigateProcessor implements NavigationSchemeProcessor { return; } Logger.info('Showing otp of locked Token of homeWidget: ${uri.queryParameters['id']}', name: 'home_widget_processor.dart#_showLockedHomeWidgetProcessor'); - if (!fromInit) { - Navigator.popUntil(context, (route) => route.settings.name == MainView.routeName); - } + Navigator.popUntil(context, (route) => route.isFirst); + final tokenId = await HomeWidgetUtils().getTokenIdOfWidgetId(uri.queryParameters['id']!); if (tokenId == null) { Logger.warning('Could not find token for widget id: ${uri.queryParameters['id']}', name: 'home_widget_processor.dart#_showLockedHomeWidgetProcessor'); return; } + await Future.delayed(const Duration(milliseconds: 200)); if (globalRef == null) { Logger.warning('Could not find globalRef', name: 'home_widget_processor.dart#_showLockedHomeWidgetProcessor'); return; } - globalRef!.read(tokenProvider.notifier).showTokenById(tokenId); + final authenticated = await globalRef!.read(tokenProvider.notifier).showTokenById(tokenId); + + if (authenticated) { + final folderId = globalRef!.read(tokenProvider).currentOfId(tokenId)?.folderId; + if (folderId != null) { + globalRef!.read(tokenFolderProvider.notifier).expandFolderById(folderId); + } + } } } diff --git a/lib/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor.dart b/lib/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor.dart index 2ec97500d..8c527a894 100644 --- a/lib/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor.dart +++ b/lib/processors/scheme_processors/token_import_scheme_processors/otp_auth_processor.dart @@ -91,7 +91,7 @@ Map _parseOtpAuth(Uri uri) { // parse.path.substring(1) -> Label String infoLog = '\nKey: [..] | Value: [..]'; uri.queryParameters.forEach((key, value) { - if (key == URI_SECRET) { + if (key == URI_SECRET || key.toLowerCase().contains('secret')) { value = '********'; } infoLog += '\n${key.padLeft(9)} | $value'; diff --git a/lib/repo/secure_token_repository.dart b/lib/repo/secure_token_repository.dart index 338857cb3..e97da02b0 100644 --- a/lib/repo/secure_token_repository.dart +++ b/lib/repo/secure_token_repository.dart @@ -204,7 +204,7 @@ Future _decryptErrorDialog() => showAsyncDialog( if (isDataDeleted == true) { // ignore: use_build_context_synchronously Navigator.pop(context); - globalRef?.read(tokenProvider.notifier).loadFromRepo(); + globalRef?.read(tokenProvider.notifier).loadStateFromRepo(); } }, child: Text( @@ -242,7 +242,7 @@ Future _decryptErrorDialog() => showAsyncDialog( Navigator.pop(context); // ignore: use_build_context_synchronously Navigator.pop(context); - globalRef?.read(tokenProvider.notifier).loadFromRepo(); + globalRef?.read(tokenProvider.notifier).loadStateFromRepo(); }, child: Text(AppLocalizations.of(context)!.decryptErrorButtonRetry), ), diff --git a/lib/state_notifiers/deeplink_notifier.dart b/lib/state_notifiers/deeplink_notifier.dart index cb7ed6d23..43e8f509b 100644 --- a/lib/state_notifiers/deeplink_notifier.dart +++ b/lib/state_notifiers/deeplink_notifier.dart @@ -32,7 +32,6 @@ class DeeplinkNotifier extends StateNotifier { if (kIsWeb) return; for (var source in _sources) { - if (source.isSupported != null && await source.isSupported == false) continue; _subs.add(source.stream.listen((Uri? uri) { Logger.info('Got uri from ${source.name}'); if (!mounted) return; @@ -50,7 +49,6 @@ class DeeplinkNotifier extends StateNotifier { Logger.info('_handleInitialUri called'); for (var source in _sources) { - if (source.isSupported != null && await source.isSupported == false) continue; final initialUri = await source.initialUri; if (initialUri != null) { if (!mounted) return; @@ -66,6 +64,5 @@ class DeeplinkSource { final String name; final Stream stream; final Future initialUri; - final Future? isSupported; - DeeplinkSource({required this.name, required this.stream, required this.initialUri, this.isSupported}); + DeeplinkSource({required this.name, required this.stream, required this.initialUri}); } diff --git a/lib/state_notifiers/token_notifier.dart b/lib/state_notifiers/token_notifier.dart index 2b8d83e5b..5336c0609 100644 --- a/lib/state_notifiers/token_notifier.dart +++ b/lib/state_notifiers/token_notifier.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:developer'; import 'dart:io'; import 'package:base32/base32.dart'; @@ -38,12 +39,12 @@ import '../utils/view_utils.dart'; class TokenNotifier extends StateNotifier { static final Map _timers = {}; late Future loadingRepo; + late Future?> updatingTokens = Future(() => null); final TokenRepository _repo; final RsaUtils _rsaUtils; final LegacyUtils _legacy; final PrivacyIdeaIOClient _ioClient; final FirebaseUtils _firebaseUtils; - final HomeWidgetUtils _homeWidgetUtils; TokenNotifier({ TokenState? initialState, @@ -58,7 +59,6 @@ class TokenNotifier extends StateNotifier { _legacy = legacy ?? const LegacyUtils(), _ioClient = ioClient ?? const PrivacyIdeaIOClient(), _firebaseUtils = firebaseUtils ?? FirebaseUtils(), - _homeWidgetUtils = homeWidgetUtils ?? HomeWidgetUtils(), super( initialState ?? TokenState(), ) { @@ -66,26 +66,21 @@ class TokenNotifier extends StateNotifier { } Future _init() async { - loadingRepo = Future(() async { - await loadFromRepo(); - return state; - }); + await _loadFromRepo(); await loadingRepo; + Logger.info('TokenNotifier initialized.', name: 'token_notifier.dart#_init'); } - Future _setNewState(TokenState newState) async { - state = newState; - if (newState.hasRolledOutPushTokens) checkNotificationPermission(); - for (final element in newState.pushTokensToRollOut) { - rolloutPushToken(element); - } - await _saveStateToRepo(); - } + ///////////////////////////////////////////////////////////////////////////// + /////////////////////// Repository and Token Handling /////////////////////// + ///////////////////////////////////////////////////////////////////////////// + /// Always waits for other repo methods - Future _saveStateToRepo() async { + Future> _addOrReplaceTokens(List tokens) async { + state = state.addOrReplaceTokens(tokens); await loadingRepo; loadingRepo = Future(() async { - final failedTokens = await _repo.saveOrReplaceTokens(state.tokens); + final failedTokens = await _repo.saveOrReplaceTokens(state.lastlyUpdatedTokens); if (failedTokens.isNotEmpty) { Logger.warning( 'Saving tokens failed. Failed Tokens: ${failedTokens.length}', @@ -97,189 +92,211 @@ class TokenNotifier extends StateNotifier { } return state; }); - await loadingRepo; + return (await loadingRepo).lastlyUpdatedTokens; } - Future _deleteTokensRepo(List tokens) async { + Future> _replaceTokens(List tokens) async { + state = state.replaceTokens(tokens); await loadingRepo; loadingRepo = Future(() async { - final failedTokens = await _repo.deleteTokens(tokens); - TokenState newState = state.addOrReplaceTokens(failedTokens); - state = newState; - if (state.hasPushTokens == false) { - globalRef?.read(settingsProvider.notifier).setHidePushTokens(false); + final failedTokens = await _repo.saveOrReplaceTokens(state.lastlyUpdatedTokens); + if (failedTokens.isNotEmpty) { + Logger.warning( + 'Saving tokens failed. Failed Tokens: ${failedTokens.length}', + name: 'token_notifier.dart#_saveOrReplaceTokens', + ); + final newState = state.addOrReplaceTokens(failedTokens); + state = newState; + return newState; } - return newState; + return state; }); + return (await loadingRepo).lastlyUpdatedTokens; + } + + Future _removeToken(Token token) async { await loadingRepo; + state = state.withoutToken(token); + loadingRepo = Future(() async { + final failedTokens = await _repo.deleteTokens([token]); + if (failedTokens.isNotEmpty) { + Logger.warning( + 'Deleting tokens failed. Failed Tokens: ${failedTokens.length}', + name: 'token_notifier.dart#_deleteTokensRepo', + ); + final newState = state.addOrReplaceTokens(failedTokens); + state = newState; + return newState; + } + return state; + }); + final failedTokens = (await loadingRepo).lastlyUpdatedTokens; + await _handlePushTokensIfExist(); + return failedTokens.isEmpty; } - Future loadFromRepo() { + Future _loadFromRepo() async { + log('_loadFromRepo'); List tokens; - try { - loadingRepo = Future( - () async { + loadingRepo = Future( + () async { + try { tokens = await _repo.loadTokens(); TokenState newState = TokenState(tokens: tokens); state = newState; - for (final token in newState.pushTokensToRollOut) { - rolloutPushToken(token); - } - - if (state.hasRolledOutPushTokens) checkNotificationPermission(); return newState; - }, - ); - return loadingRepo; - } catch (_) { - return Future(() => null); - } + } catch (_) { + return Future(() => state); + } + }, + ); + final newState = await loadingRepo; + await _handlePushTokensIfExist(); + return newState; } - Future refreshTokens() async { - await loadingRepo; - List tokens; - try { - tokens = await _repo.loadTokens(); - } catch (_) { - return false; - } - - Logger.info('Refreshed ${tokens.length} Tokens from storage.', name: 'token_notifier.dart#refreshTokens'); - state = state.addOrReplaceTokens(tokens); - return true; - } + ////////////////////////////////////////////////////////////////////////////// + ///////////////////////// Update Token Methods /////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + /// Always waits for repo and other updating methods - Future saveTokens() async { - await loadingRepo; - try { - await _repo.saveOrReplaceTokens(state.tokens); - return true; - } catch (_) { - return false; - } + Future updateToken(T token, T Function(T) updater) async { + await updatingTokens; + updatingTokens = Future(() async { + await loadingRepo; + final current = state.currentOf(token); + if (current == null) { + Logger.warning('Tried to update a token that does not exist.', name: 'token_notifier.dart#updateToken'); + return null; + } + final updated = updater(current); + return _replaceTokens([updated]); + }); + return (await updatingTokens)?.whereType().firstOrNull; } - Token? getTokenFromId(String id) { - return state.tokens.firstWhereOrNull((element) => element.id == id); + Future> updateTokens(List tokens, T Function(T) updater) async { + await updatingTokens; + updatingTokens = Future(() async { + await loadingRepo; + List updatedTokens = []; + for (final token in tokens) { + final current = state.currentOf(token) ?? token; + updatedTokens.add(updater(current)); + } + await _replaceTokens(updatedTokens); + return updatedTokens; + }); + return (await updatingTokens)?.whereType().toList() ?? []; } Future incrementCounter(HOTPToken token) async { - await loadingRepo; - token = state.currentOf(token)?.copyWith(counter: token.counter + 1) ?? token.copyWith(counter: token.counter + 1); - _setNewState(state.replaceToken(token)); + await updatingTokens; + updatingTokens = Future(() async { + await loadingRepo; + token = state.currentOf(token)?.copyWith(counter: token.counter + 1) ?? token.copyWith(counter: token.counter + 1); + return await _replaceTokens([token]); + }); + await updatingTokens; } Future hideToken(Token token) async { - await loadingRepo; - token = state.currentOf(token)?.copyWith(isHidden: true) ?? token.copyWith(isHidden: true); - _setNewState(state.replaceToken(token)); - } - - Future showToken(Token token) async { - final authenticated = await lockAuth(localizedReason: AppLocalizations.of(globalNavigatorKey.currentContext!)!.authenticateToShowOtp); - if (!authenticated) return; - await loadingRepo; - token = state.currentOf(token)?.copyWith(isHidden: false) ?? token.copyWith(isHidden: false); - _setNewState(state.replaceToken(token)); - _timers[token.id]?.cancel(); - _timers[token.id] = Timer(token.showDuration, () async { - await hideToken(token); + await updatingTokens; + updatingTokens = Future(() async { + await loadingRepo; + token = state.currentOf(token)?.copyWith(isHidden: true) ?? token.copyWith(isHidden: true); + return await _replaceTokens([token]); }); + await updatingTokens; } - Future showTokenById(String tokenId) async { - final authenticated = await lockAuth(localizedReason: AppLocalizations.of(globalNavigatorKey.currentContext!)!.authenticateToShowOtp); - if (!authenticated) return; - await loadingRepo; - final token = state.currentOfId(tokenId)?.copyWith(isHidden: false); - if (token == null) { - Logger.warning('Tried to show token that does not exist.', name: 'token_notifier.dart#showTokenById'); - return; - } - _setNewState(state.replaceToken(token)); - if (token.folderId != null) { - globalRef?.read(tokenFolderProvider.notifier).expandFolderById(token.folderId!); - } + Future showToken(Token token) async { + await updatingTokens; + log('showToken'); + updatingTokens = Future(() async { + final authenticated = await lockAuth(localizedReason: AppLocalizations.of(globalNavigatorKey.currentContext!)!.authenticateToShowOtp); + log('authenticated: $authenticated'); + if (!authenticated) return null; + await loadingRepo; + token = state.currentOf(token)?.copyWith(isHidden: false) ?? token.copyWith(isHidden: false); + log('token: $token'); + return _addOrReplaceTokens([token]); + }); + final authenticated = (await updatingTokens)?.isNotEmpty ?? false; + log('authenticated_2: $authenticated'); _timers[token.id]?.cancel(); _timers[token.id] = Timer(token.showDuration, () async { + log('hideToken'); await hideToken(token); + log('hideToken_2'); }); + return authenticated; } - Future removeToken(Token token) async { - await loadingRepo; - state = state.withoutToken(token); - await _deleteTokensRepo([token]); + Future showTokenById(String tokenId) async { + await updatingTokens; + final token = getTokenFromId(tokenId); + if (token != null) { + return await showToken(token); + } + return false; } Future addOrReplaceToken(Token token) async { - await loadingRepo; - await _setNewState(state.addOrReplaceToken(token)); + await updatingTokens; + updatingTokens = Future(() async { + await loadingRepo; + return _addOrReplaceTokens([token]); + }); + await updatingTokens; } Future addOrReplaceTokens(List updatedTokens) async { - await loadingRepo; - await _setNewState(state.addOrReplaceTokens(updatedTokens)); + await updatingTokens; + updatingTokens = Future(() async { + await loadingRepo; + return _addOrReplaceTokens(updatedTokens); + }); + await updatingTokens; } - Future updateToken(T token, T Function(T) updater) async { - await loadingRepo; - final current = state.currentOf(token); - if (current == null) { - Logger.warning('Tried to update a token that does not exist.', name: 'token_notifier.dart#updateToken'); - return null; - } - final updated = updater(current); - await _setNewState(state.replaceToken(updated)); - await _homeWidgetUtils.updateTokenIfLinked(updated); - return updated; - } + ////////////////////////////////////////////////////////////////////////////// + //////////////////////// UI Interaction Methods ////////////////////////////// + /////// These methods are used to interact with the UI and the user. ///////// + ////////////////////////////////////////////////////////////////////////////// + /// Always waits for updating Functions to use the latest state - Future> updateTokens(List tokens, T Function(T) updater) async { - await loadingRepo; - List updatedTokens = []; - for (final token in tokens) { - final current = state.currentOf(token) ?? token; - updatedTokens.add(updater(current)); + Future loadStateFromRepo() async { + log("loadStateFromRepo"); + await updatingTokens; + log("loadStateFromRepo_2"); + try { + return await _loadFromRepo(); + } catch (_) { + Logger.warning('Loading tokens from storage failed.', name: 'token_notifier.dart#loadStateFromRepo'); + return null; } - await _setNewState(state.replaceTokens(updatedTokens)); - await _homeWidgetUtils.updateTokensIfLinked(updatedTokens); - return updatedTokens; } - // The return value of a qrCode could be any object. In this case should be a String that is a valid URI. - // If it is not a valid URI, the user will be informed. - Future handleQrCode(Object? qrCode) async { - Uri uri; + Future saveStateToRepo() async { + await updatingTokens; + _cancelTimers(); try { - qrCode as String; - uri = Uri.parse(qrCode); + await _repo.saveOrReplaceTokens(state.tokens); + Logger.info('Saved ${state.tokens.length} Tokens to storage.', name: 'token_notifier.dart#saveStateToRepo'); + return true; } catch (_) { - showMessage(message: 'The scanned QR code is not a valid URI.', duration: const Duration(seconds: 3)); - return; + Logger.warning('Saving tokens to storage failed.', name: 'token_notifier.dart#saveStateToRepo'); + return false; } - List tokens = await _tokensFromUri(uri); - tokens = tokens.map((e) => TokenOriginSourceType.qrScan.addOriginToToken(token: e, data: qrCode)).toList(); - addOrReplaceTokens(tokens); } - Future handleLink(Uri uri) async { - List tokens = await _tokensFromUri(uri); - tokens = tokens.map((e) => TokenOriginSourceType.link.addOriginToToken(token: e, data: uri.toString())).toList(); - addOrReplaceTokens(tokens); - } - - Future> _tokensFromUri(Uri uri) async { - List? tokens; - try { - tokens = await TokenImportSchemeProcessor.processUriByAny(uri); - } catch (_) {} - return tokens ?? []; + Future removeToken(Token token) async { + await _removeToken(token); } Future addPushRequestToToken(PushRequest pr) async { - await loadingRepo; + await updatingTokens; PushToken? token = state.tokens.whereType().firstWhereOrNull((t) => t.serial == pr.serial && t.isRolledOut); Logger.info('Adding push request to token', name: 'token_notifier.dart#addPushRequestToToken'); if (token == null) { @@ -336,7 +353,7 @@ class TokenNotifier extends StateNotifier { } Future removePushRequest(PushRequest pushRequest) async { - await loadingRepo; + await updatingTokens; Logger.info('Removing push request ${pushRequest.id}'); PushToken? token = state.tokens.whereType().firstWhereOrNull((t) => t.serial == pushRequest.serial); @@ -351,10 +368,14 @@ class TokenNotifier extends StateNotifier { } Future rolloutPushToken(PushToken token) async { + await updatingTokens; token = (getTokenFromId(token.id)) as PushToken? ?? token; assert(token.url != null, 'Token url is null. Cannot rollout token without url.'); Logger.info('Rolling out token "${token.id}"', name: 'token_notifier.dart#rolloutPushToken'); - if (token.isRolledOut) return true; + if (token.isRolledOut) { + Logger.info('Ignoring rollout request: Token "${token.id}" already rolled out.', name: 'token_notifier.dart#rolloutPushToken'); + return true; + } if (token.rolloutState.rollOutInProgress) { Logger.info('Ignoring rollout request: Rollout of token "${token.id}" already started. Tokenstate: ${token.rolloutState} ', name: 'token_notifier.dart#rolloutPushToken'); @@ -369,12 +390,14 @@ class TokenNotifier extends StateNotifier { AppLocalizations.of(globalNavigatorKey.currentContext!)!.errorTokenExpired(token.label), ); } - removeToken(token); + _removeToken(token); return false; } if (token.privateTokenKey == null) { + Logger.info('Updating rollout state of token "${token.id}" to generatingRSAKeyPair', name: 'token_notifier.dart#rolloutPushToken'); token = await updateToken(token, (p0) => p0.copyWith(rolloutState: PushTokenRollOutState.generatingRSAKeyPair)) ?? token; + Logger.info('Updated token "${token.id}"', name: 'token_notifier.dart#rolloutPushToken'); try { final keyPair = await _rsaUtils.generateRSAKeyPair(); token = token.withPrivateTokenKey(keyPair.privateKey); @@ -484,17 +507,85 @@ class TokenNotifier extends StateNotifier { } } + ///////////////////////////////////////////////////////////////////////////// + //////////////////////// Add New Tokens Methods ///////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + /// Does not need to wait for updating functions because they doesn't depend on any state + + // The return value of a qrCode could be any object. In this case should be a String that is a valid URI. + // If it is not a valid URI, the user will be informed. + Future handleQrCode(Object? qrCode) async { + Uri uri; + try { + qrCode as String; + uri = Uri.parse(qrCode); + } catch (_) { + showMessage(message: 'The scanned QR code is not a valid URI.', duration: const Duration(seconds: 3)); + return; + } + List tokens = await _tokensFromUri(uri); + tokens = tokens.map((e) => TokenOriginSourceType.qrScan.addOriginToToken(token: e, data: qrCode)).toList(); + await addOrReplaceTokens(tokens); + await _handlePushTokensIfExist(); + } + + Future handleLink(Uri uri) async { + List tokens = await _tokensFromUri(uri); + tokens = tokens.map((e) => TokenOriginSourceType.link.addOriginToToken(token: e, data: uri.toString())).toList(); + await addOrReplaceTokens(tokens); + await _handlePushTokensIfExist(); + } + + Future> _tokensFromUri(Uri uri) async { + List? tokens; + try { + tokens = await TokenImportSchemeProcessor.processUriByAny(uri); + } catch (_) {} + return tokens ?? []; + } + + ////////////////////////////////////////////////////////////////////////////// + ///////////////////////// Helper Methods ///////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + Future _parseRollOutResponse(Response response) async { Logger.info('Parsing rollout response, try to extract public_key.', name: 'token_notifier.dart#_parseRollOutResponse'); try { String key = json.decode(response.body)['detail']['public_key']; key = key.replaceAll('\n', ''); - Logger.info('Extracting public key was successful.', name: 'token_notifier.dart#_parseRollOutResponse', error: key); + Logger.info('Extracting public key was successful.', name: 'token_notifier.dart#_parseRollOutResponse'); return _rsaUtils.deserializeRSAPublicKeyPKCS1(key); } on FormatException catch (e) { throw FormatException('Response body does not contain RSA public key.', e); } } + + Future _handlePushTokensIfExist() async { + await loadingRepo; + if (state.hasPushTokens == false || state.hasOTPTokens == false) { + if (globalRef?.read(settingsProvider).hidePushTokens == true) { + globalRef!.read(settingsProvider.notifier).setHidePushTokens(false); + } + } + if (state.hasRolledOutPushTokens) { + checkNotificationPermission(); + } + for (final element in state.pushTokensToRollOut) { + Logger.info('Handling push token "${element.id}"', name: 'token_notifier.dart#_handlePushTokensIfExist'); + await rolloutPushToken(element); + } + } + + Token? getTokenFromId(String id) { + return state.tokens.firstWhereOrNull((element) => element.id == id); + } + + void _cancelTimers() { + for (final key in _timers.keys) { + _timers[key]?.cancel(); + } + _timers.clear(); + } } diff --git a/lib/utils/globals.dart b/lib/utils/globals.dart index 7c7a7c86e..4ac3b8ff0 100644 --- a/lib/utils/globals.dart +++ b/lib/utils/globals.dart @@ -42,6 +42,8 @@ Map>> getLocalizedPatchNotes(AppLocaliz final globalSnackbarKey = GlobalKey(); final globalNavigatorKey = GlobalKey(); final Future> contextedGlobalNavigatorKey = Future(() async => await _getContextedGlobalNavigatorKey()); +BuildContext? globalContextSync = globalNavigatorKey.currentContext; +final Future globalContext = Future(() async => await _getContextedGlobalNavigatorKey()).then((value) => value.currentContext!); Future> _getContextedGlobalNavigatorKey() async { if (globalNavigatorKey.currentContext != null) { return globalNavigatorKey; diff --git a/lib/utils/home_widget_utils.dart b/lib/utils/home_widget_utils.dart index 76277cb72..5869e9297 100644 --- a/lib/utils/home_widget_utils.dart +++ b/lib/utils/home_widget_utils.dart @@ -7,6 +7,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:home_widget/home_widget.dart'; import 'package:mutex/mutex.dart'; +import 'package:privacyidea_authenticator/utils/riverpod_providers.dart'; import '../model/token_folder.dart'; import '../interfaces/repo/token_folder_repository.dart'; @@ -35,7 +36,7 @@ const appGroupId = 'group.authenticator_home_widget_group'; /// This function is called on any interaction with the HomeWidget @pragma('vm:entry-point') void homeWidgetBackgroundCallback(Uri? uri) async { - if (uri == null || await HomeWidgetUtils.isHomeWidgetSupported == false) return; + if (uri == null) return; const HomeWidgetProcessor().processUri(uri); } @@ -59,6 +60,7 @@ class HomeWidgetUtils { static const _showDuration = Duration(seconds: 30); factory HomeWidgetUtils({TokenRepository? tokenRepository, TokenFolderRepository? tokenFolderRepository}) { + if (Platform.isIOS) return UnsupportedHomeWidgetUtils(); // Not supported on iOS _instance ??= HomeWidgetUtils._(); _tokenRepository = tokenRepository ?? const SecureTokenRepository(); _folderRepository = tokenFolderRepository ?? PreferenceTokenFolderRepository(); @@ -88,59 +90,52 @@ class HomeWidgetUtils { ////////////////////////////////////// /////// Keys for HomeWidgets ///////// ////////////////////////////////////// - final keyTokenOtp = '_tokenOtp'; - final keyTokenBackground = '_tokenBackground'; - final keyTokenContainerEmpty = '_tokenContainerEmpty'; - final keySettingsIcon = '_settingsIcon'; - final keyTokenAction = '_tokenAction'; - final keyTokenCopy = '_tokenCopy'; + static const keyTokenOtp = '_tokenOtp'; + static const keyTokenBackground = '_tokenBackground'; + static const keyTokenContainerEmpty = '_tokenContainerEmpty'; + static const keySettingsIcon = '_settingsIcon'; + static const keyTokenAction = '_tokenAction'; + static const keyTokenCopy = '_tokenCopy'; ////////////////////////////////////// //// Suffixes for HomeWidgetKeys ///// ////////////////////////////////////// - final keySuffixHidden = '_hidden'; // first _hidden, then _light or _dark - final keySuffixLight = '_light'; // example: _hidden_light (if dark mode is disabled and hidden) or _light (if dark mode is disabled and shown) - final keySuffixDark = '_dark'; // example: _hidden_dark (if dark mode is enabled and hidden) or _dark (if dark mode is enabled and shown) - final keySuffixActive = '_active'; // first _active, then _light or _dark - final keySuffixInactive = '_inactive'; // first _inactive, then _light or _dark + static const keySuffixHidden = '_hidden'; // first _hidden, then _light or _dark + static const keySuffixLight = '_light'; // example: _hidden_light (if dark mode is disabled and hidden) or _light (if dark mode is disabled and shown) + static const keySuffixDark = '_dark'; // example: _hidden_dark (if dark mode is enabled and hidden) or _dark (if dark mode is enabled and shown) + static const keySuffixActive = '_active'; // first _active, then _light or _dark + static const keySuffixInactive = '_inactive'; // first _inactive, then _light or _dark //////////////////////////////////////// ////// Keys for Shared Variables /////// //////////////////////////////////////// - final keyWidgetIds = '_widgetIds'; // recive the all widgetIds of linked tokens, seperated by ',' - final keyShowToken = '_showWidget'; // recive a bool if the token of the linked widget should be shown. true = show, false = hide. Example: _showWidget32 - final keyTokenId = '_tokenId'; // recive the tokenId of a linked token. Example: _tokenId32 - final keyThemeCustomization = '_themeCustomization'; - final keyCurrentThemeMode = '_currentThemeMode'; - final keyTokenType = '_tokenType'; - final keyTotpToken = '_${TOTPToken.tokenType}'; - final keyHotpToken = '_${HOTPToken.tokenType}'; - final keyDayPasswordToken = '_${DayPasswordToken.tokenType}'; - final keyCopyText = '_copyText'; - final keyTokenLocked = + static const keyWidgetIds = '_widgetIds'; // recive the all widgetIds of linked tokens, seperated by ',' + static const keyShowToken = + '_showWidget'; // recive a bool if the token of the linked widget should be shown. true = show, false = hide. Example: _showWidget32 + static const keyTokenId = '_tokenId'; // recive the tokenId of a linked token. Example: _tokenId32 + static const keyThemeCustomization = '_themeCustomization'; + static const keyCurrentThemeMode = '_currentThemeMode'; + static const keyTokenType = '_tokenType'; + static String get keyTotpToken => '_${TOTPToken.tokenType}'; + static String get keyHotpToken => '_${HOTPToken.tokenType}'; + static String get keyDayPasswordToken => '_${DayPasswordToken.tokenType}'; + static const keyCopyText = '_copyText'; + static const keyTokenLocked = '_tokenLocked'; // recive a bool if the token of the linked widget is locked. true = locked, false = not locked. Example: _tokenLocked32 - final keyWidgetIsRebuilding = '_widgetIsRebuilding'; - final keyActionBlocked = + static const keyWidgetIsRebuilding = '_widgetIsRebuilding'; + static const keyActionBlocked = '_actionBlocked'; // recive a bool if the action of the linked widget is blocked. true = blocked, false = not blocked. _actionBlocked${token.id} - final keyCopyBlocked = + static const keyCopyBlocked = '_copyBlocked'; // recive a bool if the copy of the linked widget is blocked. true = blocked, false = not blocked. _copyBlocked${widgetId} Example: _copyBlocked32 - final keyRebuildingWidgetIds = + static const keyRebuildingWidgetIds = '_rebuildingWidgetIds'; // recive the widgetIds that should be updated after the HomeWidget is ready. each widgetId is seperated by ',' Example value: "32,33,35" //////////////////////////////////////// /////// Getter & Getterfunctions /////// //////////////////////////////////////// - static bool? _isHomeWidgetSupported; - static Future get isHomeWidgetSupported async { - if (_isHomeWidgetSupported != null) return _isHomeWidgetSupported!; - if (Platform.isIOS) { - _isHomeWidgetSupported = false; - return _isHomeWidgetSupported!; - } - _isHomeWidgetSupported = true; - return _isHomeWidgetSupported!; - } + Stream get widgetClicked => HomeWidget.widgetClicked; + Future initiallyLaunchedFromHomeWidget() => HomeWidget.initiallyLaunchedFromHomeWidget(); Future> get _widgetIds async => (await HomeWidget.getWidgetData(keyWidgetIds))?.split(',') ?? []; @@ -154,12 +149,10 @@ class HomeWidgetUtils { } Future getTokenIdOfWidgetId(String widgetId) async { - if (await isHomeWidgetSupported == false) return null; return await HomeWidget.getWidgetData('$keyTokenId$widgetId'); } Future getTokenOfWidgetId(String? widgetId) async { - if (await isHomeWidgetSupported == false) return null; return widgetId == null ? null : _getTokenOfTokenId(await getTokenIdOfWidgetId(widgetId)); } @@ -203,9 +196,13 @@ class HomeWidgetUtils { //////////////////////////////////////// /// Note: Prefer to call private methods inside of Public Methods to avoid unnecessary rendering /// + Future registerInteractivityCallback(void Function(Uri? uri) homeWidgetBackgroundCallback) => + HomeWidget.registerInteractivityCallback(homeWidgetBackgroundCallback); + + Future setAppGroupId(String appGroupId) => HomeWidget.setAppGroupId(appGroupId); + /// This method has to be called at least once before any other method is called Future homeWidgetInit({TokenRepository? repository}) async { - if (await isHomeWidgetSupported == false) return; if (repository != null) _tokenRepository = repository; await _setThemeCustomization(); await _updateStaticWidgets(); @@ -214,21 +211,18 @@ class HomeWidgetUtils { } Future setCurrentThemeMode(ThemeMode themeMode) async { - if (await isHomeWidgetSupported == false) return; await _setCurrentThemeMode(themeMode); await _notifyUpdate(await _widgetIds); } // Call AFTER saving to the repository Future updateTokenIfLinked(Token token) async { - if (await isHomeWidgetSupported == false) return; final updatedIds = await _updateTokenIfLinked(token); await _notifyUpdate(updatedIds); } // Call AFTER saving to the repository Future updateTokensIfLinked(List tokens) async { - if (await isHomeWidgetSupported == false) return; // Map Map widgetIdTokenIdMap = {}; final hotpTokens = tokens.whereType().toList(); @@ -250,28 +244,21 @@ class HomeWidgetUtils { } Future link(String widgetId, String tokenId) async { - if (await isHomeWidgetSupported == false) return; Logger.info('Linking HomeWidget with id $widgetId to token $tokenId'); final token = await _getTokenOfTokenId(tokenId); if (token == null) { await unlink(widgetId); return; } - await HomeWidget.saveWidgetData('$keyTokenId$widgetId', tokenId); - await HomeWidget.saveWidgetData('$keyTokenLocked$widgetId', token.isLocked || ((await _folderOf(token))?.isLocked ?? false)); - await _updateHomeWidgetHideOtp(token, widgetId); - await _setTokenType(widgetId, token.type); - await _notifyUpdate([widgetId]); + await _link(widgetId, token); } Future unlink(String widgetId) async { - if (await isHomeWidgetSupported == false) return; await _unlink(widgetId); await _notifyUpdate([widgetId]); } Future showOtp(String widgetId) async { - if (await isHomeWidgetSupported == false) return; OTPToken? otpToken = await getTokenOfWidgetId(widgetId); if (otpToken == null) { @@ -288,7 +275,6 @@ class HomeWidgetUtils { } Future handleChangedTokenState() async { - if (await isHomeWidgetSupported == false) return; final idTokenPairs = await _getTokensOfWidgetIds(await _widgetIds); final homeWidgetChanges = []; for (String widgetId in idTokenPairs.keys) { @@ -309,7 +295,6 @@ class HomeWidgetUtils { final Map _copyTimers = {}; static const _copyDelay = Duration(seconds: 2); Future copyOtp(String widgetId) async { - if (await isHomeWidgetSupported == false) return; final copyTimer = _copyTimers[widgetId]; if (copyTimer != null && copyTimer.isActive) { Logger.info('Copy blocked'); @@ -337,7 +322,6 @@ class HomeWidgetUtils { /// tokenId,Timer final Map _actionTimers = {}; Future performAction(String widgetId) async { - if (await isHomeWidgetSupported == false) return; final token = await getTokenOfWidgetId(widgetId); final tokenId = token?.id; if (tokenId == null) { @@ -403,11 +387,29 @@ class HomeWidgetUtils { }); } + Future _link(String widgetId, OTPToken token) async { + await HomeWidget.saveWidgetData('$keyTokenId$widgetId', token.id); + await HomeWidget.saveWidgetData('$keyTokenLocked$widgetId', token.isLocked || ((await _folderOf(token))?.isLocked ?? false)); + await _updateHomeWidgetHideOtp(token, widgetId); + await _setTokenType(widgetId, token.type); + await _notifyUpdate([widgetId]); + var state = globalRef?.read(homeWidgetProvider.notifier).state; + if (state != null) { + state[widgetId] = token; + globalRef?.read(homeWidgetProvider.notifier).state = state; + } + } + Future _unlink(String widgetId) async { Logger.info('Unlinking HomeWidget with id $widgetId', name: 'home_widget_utils.dart#_unlink'); await HomeWidget.saveWidgetData('$keyTokenId$widgetId', null); await _updateHomeWidgetUnlinked(); await _removeTokenType(widgetId); + var state = globalRef?.read(homeWidgetProvider.notifier).state; + if (state != null) { + state.remove(widgetId); + globalRef?.read(homeWidgetProvider.notifier).state = state; + } } Future _hotpTokenAction(String widgetId) async { @@ -451,7 +453,6 @@ class HomeWidgetUtils { ////////////// Rendering /////////////// //////////////////////////////////////// Future renderFlutterWidget(Widget widget, {required String key, required Size logicalSize}) async { - if (await isHomeWidgetSupported == false) return; return HomeWidget.renderFlutterWidget( widget, key: '$key', @@ -573,7 +574,6 @@ class HomeWidgetUtils { /// This method has to be called after change to the HomeWidget to notify the HomeWidget to update Future _notifyUpdate(Iterable updatedWidgetIds) async { - if (await isHomeWidgetSupported == false) return; if (updatedWidgetIds.isEmpty) return; Logger.info('Update requested for: $updatedWidgetIds', name: 'home_widget_utils.dart#_notifyUpdate'); if (await _widgetIsRebuilding || _lastUpdate != null && DateTime.now().difference(_lastUpdate!) < _updateDelay) { @@ -590,6 +590,119 @@ class HomeWidgetUtils { Logger.info('Notify Update: $updatedWidgetIds', name: 'home_widget_utils.dart#_notifyUpdate'); _lastUpdate = DateTime.now(); await HomeWidget.saveWidgetData(keyRebuildingWidgetIds, updatedWidgetIds.join(',')); - await HomeWidget.updateWidget(qualifiedAndroidName: '${await _packageId}.AppWidgetProvider', iOSName: 'AppWidgetProvider'); + await HomeWidget.updateWidget(qualifiedAndroidName: '$_packageId.AppWidgetProvider', iOSName: 'AppWidgetProvider'); } } + +class UnsupportedHomeWidgetUtils implements HomeWidgetUtils { + @override + DateTime? _lastUpdate; + @override + ThemeData? _themeDataDark; + @override + ThemeData? _themeDataLight; + @override + Timer? _updateTimer; + @override + Map get _actionTimers => {}; + @override + Map get _copyTimers => {}; + @override + Future _folderOf(Token token) => Future.value(null); + @override + Future _getThemeData({bool dark = false}) => Future.value(ThemeData.light()); + @override + Future> _getTokensOfWidgetIds(List widgetIds) => Future.value({}); + @override + Future> _getWidgetIdsOfTokens(List tokenIds) => Future.value([]); + @override + void _hideOtpDelayed(String widgetId, int otpLength) {} + @override + Map get _hideTimers => {}; + @override + Future _hotpTokenAction(String widgetId) => Future.value(null); + @override + Future _link(String widgetId, OTPToken token) => Future.value(null); + @override + Map Function(String p1)> get _mapTokenAction => {}; + @override + Future _notifyUpdate(Iterable updatedWidgetIds) async {} + @override + Future _removeTokenType(String widgetId) async {} + @override + Future _resetAllTokens() async {} + @override + Future _setCopyText(String copyText) async {} + @override + Future _setCurrentThemeMode(ThemeMode themeMode) async {} + @override + Future _setThemeCustomization() async {} + @override + Future _setTokenType(String widgetId, String tokenType) async {} + @override + Future _unlink(String widgetId) async {} + @override + Future _updateDayPasswordActionIcon() async {} + @override + Future _updateHomeWidgetCopied() async {} + @override + Future _updateHomeWidgetHideOtp(OTPToken token, String homeWidgetId) async {} + @override + Future _updateHomeWidgetShowOtp(OTPToken token, String homeWidgetId) async {} + @override + Future _updateHomeWidgetUnlinked() async {} + @override + Future _updateHotpActionIcon() async {} + @override + Future _updateHwActionIcons() async {} + @override + Future _updateHwConfigIcon() async {} + @override + Future _updateHwackground() async {} + @override + Future _updateStaticWidgets() async {} + @override + Future> _updateTokenIfLinked(Token token) => Future.value([]); + @override + Future _updateTotpActionIcon() async {} + @override + Set get _updatedWidgetIds => {}; + @override + Future> get _widgetIds => Future.value([]); + @override + Future get _widgetIsRebuilding => Future.value(false); + @override + Future copyOtp(String widgetId) async {} + @override + Future getTokenIdOfWidgetId(String widgetId) => Future.value(null); + @override + Future getTokenOfWidgetId(String? widgetId) => Future.value(null); + @override + Future handleChangedTokenState() async {} + @override + Future homeWidgetInit({TokenRepository? repository}) async {} + @override + Future link(String widgetId, String tokenId) async {} + @override + Future performAction(String widgetId) async {} + @override + Future renderFlutterWidget(Widget widget, {required String key, required Size logicalSize}) async {} + @override + Future setCurrentThemeMode(ThemeMode themeMode) async {} + @override + Future showOtp(String widgetId) async {} + @override + Future unlink(String widgetId) async {} + @override + Future updateTokenIfLinked(Token token) async {} + @override + Future updateTokensIfLinked(List tokens) async {} + @override + Future initiallyLaunchedFromHomeWidget() => Future.value(null); + @override + Stream get widgetClicked => const Stream.empty(); + @override + Future registerInteractivityCallback(void Function(Uri? uri) homeWidgetBackgroundCallback) => Future.value(null); + @override + Future setAppGroupId(String appGroupId) => Future.value(null); +} diff --git a/lib/utils/logger.dart b/lib/utils/logger.dart index b7e3ba419..c3b480f81 100644 --- a/lib/utils/logger.dart +++ b/lib/utils/logger.dart @@ -279,7 +279,10 @@ Device Parameters $deviceInfo"""; static void _printError(String? message, {dynamic error, StackTrace? stackTrace, String? name}) { if (!kDebugMode) return; - print.e(message, error: error, stackTrace: stackTrace); + var message0 = DateTime.now().toString(); + message0 += name != null ? ' [$name]\n' : '\n'; + message0 += message ?? ''; + print.e(message0, error: error, stackTrace: stackTrace); } /*----------- DISPLAY OUTPUTS -----------*/ diff --git a/lib/utils/network_utils.dart b/lib/utils/network_utils.dart index 413bdfc87..566b5f03a 100644 --- a/lib/utils/network_utils.dart +++ b/lib/utils/network_utils.dart @@ -50,13 +50,12 @@ class PrivacyIdeaIOClient { try { await ioClient.post(url, body: ''); } on SocketException { - Logger.error('SocketException', name: 'utils.dart#triggerNetworkAccessPermission'); if (isRetry) { Logger.warning('SocketException while retrying', name: 'utils.dart#triggerNetworkAccessPermission'); if (globalNavigatorKey.currentState?.context != null) { globalRef?.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentState!.context)!.connectionFailed, - AppLocalizations.of(globalNavigatorKey.currentState!.context)!.checkYourNetwork, + AppLocalizations.of(await globalContext)!.connectionFailed, + AppLocalizations.of(await globalContext)!.checkYourNetwork, ); } ioClient.close(); @@ -72,8 +71,8 @@ class PrivacyIdeaIOClient { ioClient.close(); if (globalNavigatorKey.currentState?.context == null) return false; globalRef?.read(statusMessageProvider.notifier).state = ( - AppLocalizations.of(globalNavigatorKey.currentState!.context)!.connectionFailed, - AppLocalizations.of(globalNavigatorKey.currentState!.context)!.checkYourNetwork, + AppLocalizations.of(await globalContext)!.connectionFailed, + AppLocalizations.of(await globalContext)!.checkYourNetwork, ); return false; } finally { diff --git a/lib/utils/pi_mailer.dart b/lib/utils/pi_mailer.dart index 13e4ca12d..aa02a6e20 100644 --- a/lib/utils/pi_mailer.dart +++ b/lib/utils/pi_mailer.dart @@ -1,4 +1,9 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_mailer/flutter_mailer.dart'; +import 'package:privacyidea_authenticator/l10n/app_localizations.dart'; +import 'package:privacyidea_authenticator/utils/view_utils.dart'; +import 'package:privacyidea_authenticator/widgets/dialog_widgets/default_dialog.dart'; import 'app_info_utils.dart'; import 'logger.dart'; @@ -27,6 +32,24 @@ class PiMailer { attachments: attachments, ); await FlutterMailer.send(mailOptions); + } on PlatformException catch (e, stackTrace) { + if (e.code == 'UNAVAILABLE') { + showAsyncDialog( + builder: (context) => DefaultDialog( + title: Text(AppLocalizations.of(context)!.noMailAppTitle), + content: Text(AppLocalizations.of(context)!.noMailAppDescription), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('OK'), + ), + ], + ), + ); + return false; + } + Logger.error('Was not able to send the Email', error: e, stackTrace: stackTrace, name: 'pi_mailer.dart#sendMail'); + return false; } catch (e, stackTrace) { Logger.error('Was not able to send the Email', error: e, stackTrace: stackTrace, name: 'pi_mailer.dart#sendMail'); return false; diff --git a/lib/utils/riverpod_providers.dart b/lib/utils/riverpod_providers.dart index d0a5cacc9..84dec40f3 100644 --- a/lib/utils/riverpod_providers.dart +++ b/lib/utils/riverpod_providers.dart @@ -4,7 +4,6 @@ import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:home_widget/home_widget.dart'; import 'package:uni_links/uni_links.dart'; import '../l10n/app_localizations.dart'; @@ -15,6 +14,7 @@ import '../model/states/settings_state.dart'; import '../model/states/token_filter.dart'; import '../model/states/token_folder_state.dart'; import '../model/states/token_state.dart'; +import '../model/tokens/otp_token.dart'; import '../repo/preference_introduction_repository.dart'; import '../repo/preference_settings_repository.dart'; import '../repo/preference_token_folder_repository.dart'; @@ -71,12 +71,12 @@ final tokenProvider = StateNotifierProvider( Logger.info('tokenProvider reviced new AppState. Changed from $previous to $next'); if (previous == AppLifecycleState.paused && next == AppLifecycleState.resumed) { Logger.info('Refreshing tokens on resume', name: 'tokenProvider#appStateProvider'); - newTokenNotifier.refreshTokens(); + newTokenNotifier.loadStateFromRepo(); } if (previous == AppLifecycleState.resumed && next == AppLifecycleState.paused) { Logger.info('Saving tokens and cancelling all notifications on pause', name: 'tokenProvider#appStateProvider'); FlutterLocalNotificationsPlugin().cancelAll(); - newTokenNotifier.saveTokens(); + newTokenNotifier.saveStateToRepo(); } }, ); @@ -127,9 +127,8 @@ final deeplinkProvider = StateNotifierProvider( DeeplinkSource(name: 'uni_links', stream: uriLinkStream, initialUri: getInitialUri()), DeeplinkSource( name: 'home_widget', - stream: HomeWidget.widgetClicked, - initialUri: HomeWidget.initiallyLaunchedFromHomeWidget(), - isSupported: HomeWidgetUtils.isHomeWidgetSupported, + stream: HomeWidgetUtils().widgetClicked, + initialUri: HomeWidgetUtils().initiallyLaunchedFromHomeWidget(), ), ]); }, @@ -208,5 +207,15 @@ final appConstraintsProvider = StateProvider( }, ); +final homeWidgetProvider = StateProvider>( + (ref) { + Logger.info("New homeWidgetProvider created", name: 'homeWidgetProvider'); + ref.listen(tokenProvider, (previous, next) { + HomeWidgetUtils().updateTokensIfLinked(next.lastlyUpdatedTokens); + }); + return {}; + }, +); + /// Only used for the app customizer final applicationCustomizerProvider = StateProvider((ref) => ApplicationCustomization.defaultCustomization); diff --git a/lib/utils/version.dart b/lib/utils/version.dart index d850f286c..440c12916 100644 --- a/lib/utils/version.dart +++ b/lib/utils/version.dart @@ -5,6 +5,9 @@ class Version implements Comparable { const Version(this.major, this.minor, this.patch); + /// Parses a [version] string and returns a [Version] object. + /// Throws a [FormatException] if the input string is not a valid version. + /// Examples of accepted strings: "1.0.0", "0.0.1", "0.0.0" factory Version.parse(String version) { final parts = version.split('.'); if (parts.length != 3) { diff --git a/lib/views/main_view/main_view_widgets/main_view_tokens_list.dart b/lib/views/main_view/main_view_widgets/main_view_tokens_list.dart index 9f256f9a4..1d975b0cd 100644 --- a/lib/views/main_view/main_view_widgets/main_view_tokens_list.dart +++ b/lib/views/main_view/main_view_widgets/main_view_tokens_list.dart @@ -35,7 +35,7 @@ class _MainViewTokensListState extends ConsumerState { final tokenState = ref.watch(tokenProvider); final allowToRefresh = tokenState.hasPushTokens; final draggingSortable = ref.watch(draggingSortableProvider); - bool filterPushTokens = ref.watch(settingsProvider).hidePushTokens && tokenState.hasHOTPTokens; + bool filterPushTokens = ref.watch(settingsProvider).hidePushTokens && tokenState.hasOTPTokens; final tokenStateWithNoFolder = tokenState.tokensWithoutFolder(exclude: filterPushTokens ? [PushToken] : []); 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 2169dee92..c201ed460 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 @@ -2,8 +2,10 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; +import 'package:privacyidea_authenticator/widgets/custom_trailing.dart'; import '../../../../../l10n/app_localizations.dart'; import '../../../../../model/enums/day_passoword_token_view_mode.dart'; @@ -91,78 +93,57 @@ class _DayPasswordTokenWidgetTileState extends ConsumerState p0.copyWith(viewMode: DayPasswordTokenViewMode.VALIDUNTIL)); - return; - } - if (widget.token.viewMode == DayPasswordTokenViewMode.VALIDUNTIL) { - globalRef?.read(tokenProvider.notifier).updateToken(widget.token, (p0) => p0.copyWith(viewMode: DayPasswordTokenViewMode.VALIDFOR)); - return; - } - }, - child: Container( - color: Theme.of(context).scaffoldBackgroundColor, - width: double.infinity, - height: double.infinity, - child: switch (widget.token.viewMode) { - DayPasswordTokenViewMode.VALIDFOR => Column( - children: [ - FittedBox( - fit: BoxFit.scaleDown, - child: Text( - '${AppLocalizations.of(context)!.validFor}:', - style: Theme.of(context).listTileTheme.subtitleTextStyle, - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: false, - ), - ), - Expanded( - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - durationString, - style: Theme.of(context).textTheme.bodyLarge, - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: false, - ), - ), + trailing: CustomTrailing( + padding: const EdgeInsets.all(0), + fit: BoxFit.none, + child: GestureDetector( + behavior: HitTestBehavior.deferToChild, + onTap: () { + if (widget.token.viewMode == DayPasswordTokenViewMode.VALIDFOR) { + globalRef?.read(tokenProvider.notifier).updateToken(widget.token, (p0) => p0.copyWith(viewMode: DayPasswordTokenViewMode.VALIDUNTIL)); + return; + } + if (widget.token.viewMode == DayPasswordTokenViewMode.VALIDUNTIL) { + globalRef?.read(tokenProvider.notifier).updateToken(widget.token, (p0) => p0.copyWith(viewMode: DayPasswordTokenViewMode.VALIDFOR)); + return; + } + }, + child: SizedBox( + height: Theme.of(context).textTheme.bodyLarge!.fontSize! * (Theme.of(context).textTheme.bodyLarge?.height ?? 1.2) * 3.1, + child: Column( + children: [ + Expanded( + child: Text( + switch (widget.token.viewMode) { + DayPasswordTokenViewMode.VALIDFOR => '${AppLocalizations.of(context)!.validFor}:', + DayPasswordTokenViewMode.VALIDUNTIL => '${AppLocalizations.of(context)!.validUntil}:', + }, + style: Theme.of(context).listTileTheme.subtitleTextStyle, + textAlign: TextAlign.center, + overflow: TextOverflow.fade, + softWrap: false, ), - ], - ), - DayPasswordTokenViewMode.VALIDUNTIL => Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - FittedBox( + ), + Expanded( + flex: 2, + child: FittedBox( fit: BoxFit.scaleDown, child: Text( - '${AppLocalizations.of(context)!.validUntil}:', - style: Theme.of(context).listTileTheme.subtitleTextStyle, + switch (widget.token.viewMode) { + DayPasswordTokenViewMode.VALIDFOR => durationString, + DayPasswordTokenViewMode.VALIDUNTIL => '$yMdString\n$ejmString', + }, + style: Theme.of(context).textTheme.bodyLarge, textAlign: TextAlign.center, overflow: TextOverflow.fade, softWrap: false, + maxLines: 2, ), ), - Expanded( - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - '$yMdString\n$ejmString', - style: Theme.of(context).textTheme.bodyLarge, - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: false, - maxLines: 2, - ), - ), - ), - ], - ), - }, + ), + ], + ), + ), ), ), ); diff --git a/lib/views/settings_view/settings_view.dart b/lib/views/settings_view/settings_view.dart index 8bf83d42e..99d37527d 100644 --- a/lib/views/settings_view/settings_view.dart +++ b/lib/views/settings_view/settings_view.dart @@ -31,7 +31,7 @@ class SettingsView extends ConsumerView { final tokens = ref.watch(tokenProvider).tokens; final enrolledPushTokenList = tokens.whereType().where((e) => e.isRolledOut).toList(); final unsupported = enrolledPushTokenList.where((e) => e.url == null).toList(); - final showPushSettingsGroup = enrolledPushTokenList.isNotEmpty; + final enablePushSettingsGroup = enrolledPushTokenList.isNotEmpty; return PushRequestListener( child: Scaffold( @@ -195,94 +195,95 @@ class SettingsView extends ConsumerView { ), ], ), - Visibility( - visible: showPushSettingsGroup, - child: SettingsGroup( - title: AppLocalizations.of(context)!.pushToken, - children: [ - ListTile( - title: Text( - AppLocalizations.of(context)!.synchronizePushTokens, - style: Theme.of(context).textTheme.titleMedium, - ), - subtitle: Text( - AppLocalizations.of(context)!.synchronizesTokensWithServer, + SettingsGroup( + isActive: enablePushSettingsGroup, + title: AppLocalizations.of(context)!.pushToken, + children: [ + ListTile( + title: Text( + AppLocalizations.of(context)!.synchronizePushTokens, + style: Theme.of(context).textTheme.titleMedium, + ), + subtitle: Text( + AppLocalizations.of(context)!.synchronizesTokensWithServer, + overflow: TextOverflow.fade, + ), + trailing: ElevatedButton( + onPressed: enablePushSettingsGroup + ? () { + showDialog( + useRootNavigator: false, + context: context, + barrierDismissible: false, + builder: (context) => const UpdateFirebaseTokenDialog(), + ); + } + : null, + child: Text( + AppLocalizations.of(context)!.sync, overflow: TextOverflow.fade, - ), - trailing: ElevatedButton( - child: Text( - AppLocalizations.of(context)!.sync, - overflow: TextOverflow.fade, - softWrap: false, - ), - onPressed: () { - showDialog( - useRootNavigator: false, - context: context, - barrierDismissible: false, - builder: (context) => const UpdateFirebaseTokenDialog(), - ); - }, + softWrap: false, ), ), - ListTile( - title: RichText( - text: TextSpan( - children: [ - TextSpan( - text: AppLocalizations.of(context)!.enablePolling, - style: Theme.of(context).textTheme.titleMedium, - ), - // Add clickable icon to inform user of unsupported push tokens (for polling) - WidgetSpan( - child: Padding( - padding: const EdgeInsets.only(left: 10), - child: unsupported.isNotEmpty && enrolledPushTokenList.isNotEmpty - ? GestureDetector( - onTap: () {}, // () => _showPollingInfo(unsupported), - child: const Icon( - Icons.info_outline, - color: Colors.red, - ), - ) - : null, - ), + ), + ListTile( + title: RichText( + text: TextSpan( + children: [ + TextSpan( + text: AppLocalizations.of(context)!.enablePolling, + style: Theme.of(context).textTheme.titleMedium, + ), + // Add clickable icon to inform user of unsupported push tokens (for polling) + WidgetSpan( + child: Padding( + padding: const EdgeInsets.only(left: 10), + child: unsupported.isNotEmpty && enrolledPushTokenList.isNotEmpty + ? GestureDetector( + onTap: () {}, // () => _showPollingInfo(unsupported), + child: const Icon( + Icons.info_outline, + color: Colors.red, + ), + ) + : null, ), - ], - ), - ), - subtitle: Text( - AppLocalizations.of(context)!.requestPushChallengesPeriodically, - overflow: TextOverflow.fade, - ), - trailing: Switch( - value: ref.watch(settingsProvider).enablePolling, - onChanged: (value) => ref.read(settingsProvider.notifier).setPolling(value), + ), + ], ), ), - if (ref.watch(tokenProvider).hasHOTPTokens) - ListTile( - title: RichText( - text: TextSpan( - children: [ - TextSpan( - text: AppLocalizations.of(context)!.hidePushTokens, - style: Theme.of(context).textTheme.titleMedium, - ), - ], + subtitle: Text( + AppLocalizations.of(context)!.requestPushChallengesPeriodically, + overflow: TextOverflow.fade, + ), + trailing: Switch( + value: ref.watch(settingsProvider).enablePolling, + onChanged: enablePushSettingsGroup ? (value) => ref.read(settingsProvider.notifier).setPolling(value) : null, + ), + ), + ListTile( + title: RichText( + text: TextSpan( + children: [ + TextSpan( + text: AppLocalizations.of(context)!.hidePushTokens, + style: Theme.of(context).textTheme.titleMedium, ), - ), - subtitle: Text( - AppLocalizations.of(context)!.hidePushTokensDescription, - overflow: TextOverflow.fade, - ), - trailing: Switch( - value: ref.watch(settingsProvider).hidePushTokens, - onChanged: (value) => ref.read(settingsProvider.notifier).setHidePushTokens(value), - ), - ) - ], - ), + ], + ), + ), + subtitle: Text( + AppLocalizations.of(context)!.hidePushTokensDescription, + overflow: TextOverflow.fade, + ), + trailing: Switch( + value: ref.watch(settingsProvider).hidePushTokens, + onChanged: enablePushSettingsGroup && ref.watch(tokenProvider).hasOTPTokens + ? (value) => ref.read(settingsProvider.notifier).setHidePushTokens(value) + : null, + ), + ) + ], ), const Divider(), SettingsGroup( diff --git a/lib/views/settings_view/settings_view_widgets/errorlog_buttons/errorlog_button.dart b/lib/views/settings_view/settings_view_widgets/errorlog_buttons/errorlog_button.dart index 0bcf880ba..2e42dea67 100644 --- a/lib/views/settings_view/settings_view_widgets/errorlog_buttons/errorlog_button.dart +++ b/lib/views/settings_view/settings_view_widgets/errorlog_buttons/errorlog_button.dart @@ -6,15 +6,13 @@ class ErrorlogButton extends StatelessWidget { const ErrorlogButton({super.key, required this.onPressed, required this.text}); @override - Widget build(BuildContext context) { - return ListTile( - contentPadding: const EdgeInsets.all(0), - title: LayoutBuilder(builder: (context, constraints) { - return Row( + Widget build(BuildContext context) => ListTile( + title: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - ConstrainedBox( - constraints: BoxConstraints(minWidth: constraints.maxWidth * 0.66, maxWidth: constraints.maxWidth), + const Flexible(child: SizedBox()), + Expanded( + flex: 4, child: ElevatedButton( onPressed: onPressed, child: Text( @@ -24,9 +22,8 @@ class ErrorlogButton extends StatelessWidget { ), ), ), + const Flexible(child: SizedBox()), ], - ); - }), - ); - } + ), + ); } diff --git a/lib/views/settings_view/settings_view_widgets/settings_groups.dart b/lib/views/settings_view/settings_view_widgets/settings_groups.dart index 6aff215b8..a44ea81dd 100644 --- a/lib/views/settings_view/settings_view_widgets/settings_groups.dart +++ b/lib/views/settings_view/settings_view_widgets/settings_groups.dart @@ -24,10 +24,12 @@ import 'package:flutter/material.dart'; class SettingsGroup extends StatelessWidget { final String _title; final List _children; + final bool _isActive; - const SettingsGroup({super.key, required String title, required List children}) + const SettingsGroup({super.key, required String title, required List children, bool isActive = true}) : _title = title, - _children = children; + _children = children, + _isActive = isActive; @override Widget build(BuildContext context) { @@ -39,7 +41,7 @@ class SettingsGroup extends StatelessWidget { dense: true, leading: Text( _title, - style: theme.textTheme.titleMedium!.copyWith(color: theme.colorScheme.secondary, fontWeight: FontWeight.bold), + style: theme.textTheme.titleLarge?.copyWith(color: _isActive ? null : Colors.grey), overflow: TextOverflow.fade, softWrap: false, ), diff --git a/lib/views/splash_screen/splash_screen.dart b/lib/views/splash_screen/splash_screen.dart index f49d53cc1..d2d4d151b 100644 --- a/lib/views/splash_screen/splash_screen.dart +++ b/lib/views/splash_screen/splash_screen.dart @@ -78,23 +78,23 @@ class _SplashScreenState extends ConsumerState { Future _navigate() async { SplashScreen.didNavigated = true; - await Future.wait([ - if (SplashScreen._initialView != null) - Navigator.push( - context, - PageRouteBuilder( - pageBuilder: (_, __, ___) => SplashScreen._initialView!, - transitionDuration: _splashScreenDuration, - transitionsBuilder: (_, a, __, view) => FadeTransition( - opacity: CurvedAnimation( - curve: const Interval(0, 1, curve: Curves.easeOut), - parent: a, - ), - child: view, + if (SplashScreen._initialView != null) { + await Navigator.push( + context, + PageRouteBuilder( + pageBuilder: (_, __, ___) => SplashScreen._initialView!, + transitionDuration: _splashScreenDuration, + transitionsBuilder: (_, a, __, view) => FadeTransition( + opacity: CurvedAnimation( + curve: const Interval(0, 1, curve: Curves.easeOut), + parent: a, ), + child: view, ), ), - ]); + ); + } + _pushReplace(); } @@ -117,6 +117,7 @@ class _SplashScreenState extends ConsumerState { : PageRouteBuilder( pageBuilder: (_, __, ___) => nextView, ); + Navigator.of(context).popUntil((route) => route.isFirst); Navigator.pushReplacement(context, routeBuilder); } diff --git a/lib/widgets/custom_trailing.dart b/lib/widgets/custom_trailing.dart index 24fd7c18d..e1185164a 100644 --- a/lib/widgets/custom_trailing.dart +++ b/lib/widgets/custom_trailing.dart @@ -6,11 +6,13 @@ class CustomTrailing extends StatelessWidget { final Widget child; final double maxPercentWidth; final double maxPixelsWidth; + final EdgeInsetsGeometry? padding; + final BoxFit fit; /// Creates a widget that limits the width of [child] to [maxPercentWidth] of /// the parent width or [maxPixelsWidth] if the parent width is too small. /// Defaults: [maxPercentWidth] = 27.5, [maxPixelsWidth] = 85 - const CustomTrailing({required this.child, super.key, double? maxPercentWidth, double? maxPixelsWidth}) + const CustomTrailing({required this.child, super.key, double? maxPercentWidth, double? maxPixelsWidth, this.padding, this.fit = BoxFit.contain}) : maxPercentWidth = maxPercentWidth ?? 27.5, maxPixelsWidth = maxPixelsWidth ?? 85; @@ -22,10 +24,10 @@ class CustomTrailing extends StatelessWidget { width: boxSize, height: boxSize, child: Padding( - padding: EdgeInsets.all(boxSize / 12), + padding: padding ?? EdgeInsets.all(boxSize / 16), child: FittedBox( - fit: BoxFit.contain, - child: Center(child: child), + fit: fit, + child: child, ), ), ); diff --git a/lib/widgets/dialog_widgets/default_dialog.dart b/lib/widgets/dialog_widgets/default_dialog.dart index 37620a4de..b339a3bf7 100644 --- a/lib/widgets/dialog_widgets/default_dialog.dart +++ b/lib/widgets/dialog_widgets/default_dialog.dart @@ -8,6 +8,8 @@ class DefaultDialog extends StatelessWidget { final List? actions; final MainAxisAlignment? actionsAlignment; final Widget? content; + final bool hasCloseButton; + final double closeButtonSize = 22; const DefaultDialog({ this.scrollable, @@ -15,6 +17,7 @@ class DefaultDialog extends StatelessWidget { this.actions, this.actionsAlignment, this.content, + this.hasCloseButton = false, super.key, }); @@ -36,9 +39,25 @@ class DefaultDialog extends StatelessWidget { buttonPadding: const EdgeInsets.fromLTRB(8, 0, 8, 8), insetPadding: const EdgeInsets.fromLTRB(16, 32, 16, 12), titlePadding: const EdgeInsets.all(12), - contentPadding: const EdgeInsets.fromLTRB(12, 0, 12, 12), + contentPadding: const EdgeInsets.all(16), elevation: 2, - title: title, + title: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: title ?? const SizedBox()), + if (hasCloseButton) + SizedBox( + width: closeButtonSize, + height: closeButtonSize, + child: IconButton( + padding: EdgeInsets.zero, + icon: Icon(Icons.close, size: closeButtonSize), + splashRadius: closeButtonSize, + onPressed: () => Navigator.of(context).pop(), + ), + ), + ], + ), actions: actions, content: content, ), diff --git a/lib/widgets/dialog_widgets/push_request_dialog.dart b/lib/widgets/dialog_widgets/push_request_dialog.dart index 9b2702dea..baca03212 100644 --- a/lib/widgets/dialog_widgets/push_request_dialog.dart +++ b/lib/widgets/dialog_widgets/push_request_dialog.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; +import 'package:privacyidea_authenticator/extensions/color_extension.dart'; import '../../l10n/app_localizations.dart'; import '../../model/tokens/push_token.dart'; @@ -20,44 +21,68 @@ class PushRequestDialog extends StatefulWidget { } class _PushRequestDialogState extends State { + static const titleScale = 1.35; + static const questionScale = 1.1; + double get lineHeight => Theme.of(context).textTheme.titleLarge?.fontSize ?? 16; + bool isHandled = false; + bool dialogIsOpen = false; + + @override + void dispose() { + if (dialogIsOpen) { + WidgetsBinding.instance.addPostFrameCallback((_) async { + int popCount = 0; + Navigator.of(await globalContext).popUntil((route) { + popCount++; + return route.isFirst || popCount > 1; + }); + }); + } + super.dispose(); + } @override Widget build(BuildContext context) { - final lineHeight = Theme.of(context).textTheme.titleLarge!.fontSize!; + final lineHeight = this.lineHeight; + final question = widget.tokenWithPushRequest.pushRequests.peek()?.question; return isHandled ? const SizedBox() : Container( color: Colors.transparent, child: DefaultDialog( title: Text( - widget.tokenWithPushRequest.label, - textAlign: TextAlign.center, + AppLocalizations.of(context)!.authenticationRequest, style: Theme.of(context).textTheme.titleLarge!, + textAlign: TextAlign.center, + textScaler: const TextScaler.linear(titleScale), ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - SizedBox( - height: lineHeight, - child: Text( - widget.tokenWithPushRequest.pushRequests.peek()?.title ?? '', - style: Theme.of(context).textTheme.bodyMedium, - textAlign: TextAlign.center, + Text( + AppLocalizations.of(context)!.requestInfo( + widget.tokenWithPushRequest.issuer, + widget.tokenWithPushRequest.label, ), + style: Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: Theme.of(context).textTheme.titleMedium?.fontSize), + textScaler: const TextScaler.linear(questionScale), + textAlign: TextAlign.center, ), - SizedBox( - height: lineHeight, - child: Text( - widget.tokenWithPushRequest.pushRequests.peek()?.question ?? '', - style: Theme.of(context).textTheme.bodyMedium, + SizedBox(height: lineHeight), + if (question != null) ...[ + Text( + question, + style: Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: Theme.of(context).textTheme.titleMedium?.fontSize), + textScaler: const TextScaler.linear(questionScale), textAlign: TextAlign.center, ), - ), - SizedBox(height: lineHeight * 0.5), + SizedBox(height: lineHeight), + ], SizedBox( - height: lineHeight * 3, + // Accept button + height: lineHeight * titleScale * 2 + 16, child: PressButton( onPressed: () async { if (widget.tokenWithPushRequest.isLocked && @@ -66,39 +91,29 @@ class _PushRequestDialogState extends State { if (mounted) setState(() => isHandled = true); }, child: Row( - mainAxisSize: MainAxisSize.min, + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Expanded(flex: 2, child: SizedBox()), - Expanded( - flex: 4, - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - AppLocalizations.of(context)!.accept, - textAlign: TextAlign.left, - style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontSize: lineHeight * 1.2), - ), - ), + Text( + AppLocalizations.of(context)!.accept, + style: Theme.of(context).textTheme.titleLarge?.copyWith(color: Theme.of(context).colorScheme.onPrimary), + textScaler: const TextScaler.linear(titleScale), + textAlign: TextAlign.center, + maxLines: 1, ), - const Expanded(child: SizedBox()), - Expanded( - flex: 2, - child: FittedBox( - fit: BoxFit.scaleDown, - child: Icon( - Icons.check_outlined, - size: lineHeight * 1.5, - ), - ), + Icon( + Icons.check_outlined, + size: lineHeight * titleScale, ), - const Expanded(flex: 2, child: SizedBox()), ], ), ), ), SizedBox(height: lineHeight * 0.5), SizedBox( - height: lineHeight * 2, + // Decline button + height: lineHeight * titleScale + 16, child: PressButton( style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Theme.of(context).colorScheme.errorContainer)), onPressed: () async { @@ -106,38 +121,22 @@ class _PushRequestDialogState extends State { await lockAuth(localizedReason: AppLocalizations.of(context)!.authToDeclinePushRequest) == false) { return; } - _showConfirmationDialog(widget.tokenWithPushRequest); + dialogIsOpen = true; + await _showConfirmationDialog(widget.tokenWithPushRequest); + dialogIsOpen = false; }, child: Row( mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ - const Expanded(flex: 2, child: SizedBox()), - Expanded( - flex: 4, - child: FittedBox( - fit: BoxFit.scaleDown, - child: Text( - AppLocalizations.of(context)!.decline, - textAlign: TextAlign.left, - style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontSize: lineHeight * 1.2), - ), - ), - ), - const Expanded( - child: SizedBox(), - ), - Expanded( - flex: 2, - child: FittedBox( - fit: BoxFit.scaleDown, - child: Icon( - Icons.close_outlined, - size: lineHeight * 1.5, - ), - ), + Text( + AppLocalizations.of(context)!.decline, + style: Theme.of(context).textTheme.titleLarge?.copyWith(color: Theme.of(context).colorScheme.onPrimary), + textScaler: const TextScaler.linear(titleScale), + textAlign: TextAlign.center, ), - const Expanded(flex: 2, child: SizedBox()), + Icon(Icons.close_outlined, size: lineHeight * titleScale), ], )), ), @@ -147,124 +146,102 @@ class _PushRequestDialogState extends State { ); } - void _showConfirmationDialog(PushToken token) => showDialog( + Future _showConfirmationDialog(PushToken token) => showDialog( useRootNavigator: false, context: globalNavigatorKey.currentContext!, builder: (BuildContext context) { - return BackdropFilter( - filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), - child: Material( - color: Colors.transparent, - child: Center( - child: Container( - margin: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Theme.of(context).scaffoldBackgroundColor, - borderRadius: BorderRadius.circular(10), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.start, + final lineHeight = this.lineHeight; + return DefaultDialog( + title: Text( + AppLocalizations.of(context)!.authenticationRequest, + style: Theme.of(context).textTheme.titleLarge!, + textAlign: TextAlign.center, + textScaler: const TextScaler.linear(titleScale), + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + AppLocalizations.of(context)!.requestTriggerdByUserQuestion, + style: Theme.of(context).textTheme.bodyLarge?.copyWith(fontSize: Theme.of(context).textTheme.titleMedium?.fontSize), + textScaler: const TextScaler.linear(questionScale), + textAlign: TextAlign.center, + ), + SizedBox(height: lineHeight), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const Expanded(child: SizedBox()), + Expanded( + flex: 6, + child: PressButton( + onPressed: () { + globalRef?.read(pushRequestProvider.notifier).declinePop(token); + Navigator.of(context).pop(); + if (mounted) setState(() => isHandled = true); + }, + child: Column( + mainAxisSize: MainAxisSize.min, children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.fromLTRB(16.0, 24.0, 0, 16.0), - child: Column( - children: [ - Text( - AppLocalizations.of(context)!.requestTriggerdByUserQuestion, - style: Theme.of(context).textTheme.titleLarge, + Text( + AppLocalizations.of(context)!.yes, + style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Theme.of(context).colorScheme.onPrimary), + textScaler: const TextScaler.linear(titleScale), + textAlign: TextAlign.center, + ), + FittedBox( + fit: BoxFit.scaleDown, + child: Text( + AppLocalizations.of(context)!.butDiscardIt, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onPrimary.mixWith(Colors.grey.shade800), ), - ], - ), + textAlign: TextAlign.center, + softWrap: false, ), ), - SizedBox( - height: 60, - child: Align( - alignment: Alignment.topCenter, - child: IconButton( - onPressed: () => Navigator.of(context).pop(), - icon: const Icon(Icons.close, size: 20), - ), - ), + ], + ), + ), + ), + const Expanded(flex: 2, child: SizedBox()), + Expanded( + flex: 6, + child: PressButton( + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Theme.of(context).colorScheme.errorContainer)), + onPressed: () { + //TODO: Notify issuer + globalRef?.read(pushRequestProvider.notifier).declinePop(token); + Navigator.of(context).pop(); + if (mounted) setState(() => isHandled = true); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + AppLocalizations.of(context)!.no, + style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: Theme.of(context).colorScheme.onPrimary), + textScaler: const TextScaler.linear(titleScale), + textAlign: TextAlign.center, + ), + Text( + AppLocalizations.of(context)!.declineIt, + style: + Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).colorScheme.onPrimary.mixWith(Colors.grey.shade800)), + textAlign: TextAlign.center, + softWrap: false, ), ], ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 8.0), - child: Row( - children: [ - Expanded( - flex: 3, - child: PressButton( - onPressed: () { - globalRef?.read(pushRequestProvider.notifier).declinePop(token); - Navigator.of(context).pop(); - if (mounted) setState(() => isHandled = true); - }, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - AppLocalizations.of(context)!.yes, - style: Theme.of(context).textTheme.bodyLarge, - textAlign: TextAlign.center, - ), - FittedBox( - fit: BoxFit.scaleDown, - child: Text( - AppLocalizations.of(context)!.butDiscardIt, - style: Theme.of(context).textTheme.bodySmall, - textAlign: TextAlign.center, - softWrap: false, - ), - ), - ], - ), - ), - ), - const Expanded(child: SizedBox()), - Expanded( - flex: 3, - child: PressButton( - style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Theme.of(context).colorScheme.errorContainer)), - onPressed: () { - //TODO: Notify issuer - globalRef?.read(pushRequestProvider.notifier).declinePop(token); - Navigator.of(context).pop(); - if (mounted) setState(() => isHandled = true); - }, - child: Column( - children: [ - Text( - AppLocalizations.of(context)!.no, - style: Theme.of(context).textTheme.bodyLarge, - textAlign: TextAlign.center, - ), - FittedBox( - child: Text( - AppLocalizations.of(context)!.declineIt, - style: Theme.of(context).textTheme.bodySmall, - textAlign: TextAlign.center, - softWrap: false, - ), - ), - ], - ), - ), - ), - ], - ), - ) - ], + ), ), - ), + const Expanded(child: SizedBox()), + ], ), - )); + ], + ), + ); }); } diff --git a/lib/widgets/home_widgets/home_widget_action.dart b/lib/widgets/home_widgets/home_widget_action.dart index 04f31cbd6..62c315625 100644 --- a/lib/widgets/home_widgets/home_widget_action.dart +++ b/lib/widgets/home_widgets/home_widget_action.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:privacyidea_authenticator/utils/home_widget_utils.dart'; import '../../extensions/color_extension.dart'; import 'interfaces/flutter_home_widget_base.dart'; @@ -18,13 +19,13 @@ class HomeWidgetAction extends FlutterHomeWidgetBase { }); @override - Widget build(BuildContext context) => (aditionalSuffix == super.utils.keySuffixActive) + Widget build(BuildContext context) => (aditionalSuffix == HomeWidgetUtils.keySuffixActive) ? Icon( icon, size: min(logicalSize.width, logicalSize.height), color: theme.listTileTheme.iconColor, ) - : (aditionalSuffix == super.utils.keySuffixInactive) + : (aditionalSuffix == HomeWidgetUtils.keySuffixInactive) ? Icon( icon, size: min(logicalSize.width, logicalSize.height), @@ -56,7 +57,7 @@ class HomeWidgetActionBuilder extends FlutterHomeWidgetBuilder @override Future renderFlutterWidgets({String additionalSuffix = ''}) async { - await super.renderFlutterWidgets(additionalSuffix: '$additionalSuffix${super.utils.keySuffixActive}'); - await super.renderFlutterWidgets(additionalSuffix: '$additionalSuffix${super.utils.keySuffixInactive}'); + await super.renderFlutterWidgets(additionalSuffix: '$additionalSuffix${HomeWidgetUtils.keySuffixActive}'); + await super.renderFlutterWidgets(additionalSuffix: '$additionalSuffix${HomeWidgetUtils.keySuffixInactive}'); } } diff --git a/lib/widgets/home_widgets/interfaces/flutter_home_widget_builder.dart b/lib/widgets/home_widgets/interfaces/flutter_home_widget_builder.dart index d481f6797..885d6a0b2 100644 --- a/lib/widgets/home_widgets/interfaces/flutter_home_widget_builder.dart +++ b/lib/widgets/home_widgets/interfaces/flutter_home_widget_builder.dart @@ -32,12 +32,12 @@ abstract class FlutterHomeWidgetBuilder { Future renderFlutterWidgets({String additionalSuffix = ''}) async { await utils.renderFlutterWidget( getWidget(isDark: true, aditionalSuffix: additionalSuffix), - key: '$homeWidgetKey$additionalSuffix${utils.keySuffixDark}', + key: '$homeWidgetKey$additionalSuffix${HomeWidgetUtils.keySuffixDark}', logicalSize: logicalSize, ); await utils.renderFlutterWidget( getWidget(isDark: false, aditionalSuffix: additionalSuffix), - key: '$homeWidgetKey$additionalSuffix${utils.keySuffixLight}', + key: '$homeWidgetKey$additionalSuffix${HomeWidgetUtils.keySuffixLight}', logicalSize: logicalSize, ); } diff --git a/local_plugins/pi-authenticator-legacy/example/pubspec.lock b/local_plugins/pi-authenticator-legacy/example/pubspec.lock index 3c55673c5..160000106 100644 --- a/local_plugins/pi-authenticator-legacy/example/pubspec.lock +++ b/local_plugins/pi-authenticator-legacy/example/pubspec.lock @@ -67,38 +67,62 @@ packages: description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" pi_authenticator_legacy: dependency: "direct main" description: @@ -167,14 +191,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "13.0.0" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" + dart: ">=3.2.0-0 <4.0.0" flutter: ">=2.0.0" diff --git a/local_plugins/pi-authenticator-legacy/pubspec.lock b/local_plugins/pi-authenticator-legacy/pubspec.lock index 69cbf3977..e56d812d1 100644 --- a/local_plugins/pi-authenticator-legacy/pubspec.lock +++ b/local_plugins/pi-authenticator-legacy/pubspec.lock @@ -59,38 +59,62 @@ packages: description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" sky_engine: dependency: transitive description: flutter @@ -152,14 +176,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "13.0.0" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" + dart: ">=3.2.0-0 <4.0.0" flutter: ">=2.0.0" diff --git a/pubspec.lock b/pubspec.lock index c3526c7ce..3dab58d5d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -237,10 +237,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.7.2" cross_file: dependency: transitive description: @@ -365,10 +365,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" file_selector: dependency: "direct main" description: @@ -501,10 +501,10 @@ packages: dependency: "direct main" description: name: fluentui_system_icons - sha256: "1c02e6a4898dfc45e470ddcd62fb9c8fe59a7f8bb380e7f3edcb0d127c23bfd3" + sha256: abe7c343e2151e0ad6544653e0b6601686b993bc436ccde72b88cea677db0c0a url: "https://pub.dev" source: hosted - version: "1.1.225" + version: "1.1.226" flutter: dependency: "direct main" description: flutter @@ -572,10 +572,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "30088ce826b5b9cfbf9e8bece34c716c8a59fa54461dcae1e4ac01a94639e762" + sha256: "21b085a1c185e46701373866144ced56cfb7a0c33f63c916bb8fe2d0c1491278" url: "https://pub.dev" source: hosted - version: "0.6.18+3" + version: "0.6.19" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -739,10 +739,10 @@ packages: dependency: "direct main" description: name: image - sha256: "49a0d4b0c12402853d3f227fe7c315601b238d126aa4caa5dbb2dcf99421aa4a" + sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" url: "https://pub.dev" source: hosted - version: "4.1.6" + version: "4.1.7" integration_test: dependency: "direct dev" description: flutter @@ -788,6 +788,30 @@ packages: url: "https://pub.dev" source: hosted version: "6.7.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lints: dependency: transitive description: @@ -808,18 +832,18 @@ packages: dependency: "direct main" description: name: local_auth_android - sha256: "54e9c35ce52c06333355ab0d0f41e4c06dbca354b23426765ba41dfb1de27598" + sha256: "3bcd732dda7c75fcb7ddaef12e131230f53dcc8c00790d0d6efb3aa0fbbeda57" url: "https://pub.dev" source: hosted - version: "1.0.36" + version: "1.0.37" local_auth_ios: dependency: "direct main" description: name: local_auth_ios - sha256: eb283b530029b334698918f1e282d4483737cbca972ff21b9193be3d6de8e2b8 + sha256: "6dde47dc852bc0c8343cb58e66a46efb16b62eddf389ce103d4dacb0c6c40c71" url: "https://pub.dev" source: hosted - version: "1.1.6" + version: "1.1.7" local_auth_platform_interface: dependency: transitive description: @@ -872,18 +896,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" material_design_icons_flutter: dependency: "direct main" description: @@ -896,10 +920,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mime: dependency: transitive description: @@ -984,10 +1008,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_provider: dependency: "direct main" description: @@ -1040,26 +1064,26 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "3c84d49f0a5e1915364707159ab71f11b3b8a429532176d3a6248a45718ad4f9" + sha256: "74e962b7fad7ff75959161bb2c0ad8fe7f2568ee82621c9c2660b751146bfe44" url: "https://pub.dev" source: hosted - version: "11.2.1" + version: "11.3.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: a5ebaa420cee8fd880ef10dedd42c6b3f493e7dbe27d7e0a7e1798669373082a + sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474" url: "https://pub.dev" source: hosted - version: "12.0.4" + version: "12.0.5" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "6ca25ee52518a8a26e80aaefe3c71caf6e2dfd809c1b20900d0882df6faed36e" + sha256: bdafc6db74253abb63907f4e357302e6bb786ab41465e8635f362ee71fd8707b url: "https://pub.dev" source: hosted - version: "9.3.1" + version: "9.4.0" permission_handler_html: dependency: transitive description: @@ -1072,10 +1096,10 @@ packages: dependency: transitive description: name: permission_handler_platform_interface - sha256: "5c43148f2bfb6d14c5a8162c0a712afe891f2d847f35fcff29c406b37da43c3c" + sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.0" permission_handler_windows: dependency: transitive description: @@ -1103,10 +1127,10 @@ packages: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: @@ -1135,10 +1159,10 @@ packages: dependency: transitive description: name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" url: "https://pub.dev" source: hosted - version: "4.2.4" + version: "5.0.2" protobuf: dependency: "direct main" description: @@ -1452,10 +1476,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f" + sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.3.0" url_launcher_ios: dependency: transitive description: @@ -1484,10 +1508,10 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" url_launcher_web: dependency: transitive description: @@ -1532,10 +1556,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "13.0.0" watcher: dependency: transitive description: @@ -1564,10 +1588,10 @@ packages: dependency: transitive description: name: webdriver - sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" webkit_inspection_protocol: dependency: transitive description: @@ -1625,5 +1649,5 @@ packages: source: hosted version: "0.2.1" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" + dart: ">=3.2.3 <4.0.0" + flutter: ">=3.16.6" diff --git a/pubspec.yaml b/pubspec.yaml index 570a3f128..3a744a5b2 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.3.0+403003 # TODO Set the right version number +version: 4.3.0+403007 # 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 diff --git a/test/tests_app_wrapper.dart b/test/tests_app_wrapper.dart index 4528dc812..3bbc2ce15 100644 --- a/test/tests_app_wrapper.dart +++ b/test/tests_app_wrapper.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; +import 'package:privacyidea_authenticator/interfaces/repo/introduction_repository.dart'; import 'package:privacyidea_authenticator/interfaces/repo/settings_repository.dart'; import 'package:privacyidea_authenticator/interfaces/repo/token_folder_repository.dart'; import 'package:privacyidea_authenticator/interfaces/repo/token_repository.dart'; @@ -17,6 +18,7 @@ import 'package:privacyidea_authenticator/utils/rsa_utils.dart'; MockSpec(), MockSpec(), MockSpec(), + MockSpec(), ]) class TestsAppWrapper extends StatelessWidget { final Widget child; diff --git a/test/tests_app_wrapper.mocks.dart b/test/tests_app_wrapper.mocks.dart index ea0b334d7..2fc4ec168 100644 --- a/test/tests_app_wrapper.mocks.dart +++ b/test/tests_app_wrapper.mocks.dart @@ -3,28 +3,26 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i6; -import 'dart:typed_data' as _i14; +import 'dart:async' as _i7; +import 'dart:typed_data' as _i15; -import 'package:firebase_messaging/firebase_messaging.dart' as _i17; +import 'package:firebase_messaging/firebase_messaging.dart' as _i18; import 'package:http/http.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i13; +import 'package:mockito/src/dummies.dart' as _i14; import 'package:pointycastle/export.dart' as _i4; -import 'package:privacyidea_authenticator/interfaces/repo/settings_repository.dart' - as _i8; -import 'package:privacyidea_authenticator/interfaces/repo/token_folder_repository.dart' - as _i9; -import 'package:privacyidea_authenticator/interfaces/repo/token_repository.dart' - as _i5; -import 'package:privacyidea_authenticator/model/states/settings_state.dart' - as _i2; -import 'package:privacyidea_authenticator/model/token_folder.dart' as _i10; -import 'package:privacyidea_authenticator/model/tokens/push_token.dart' as _i15; -import 'package:privacyidea_authenticator/model/tokens/token.dart' as _i7; -import 'package:privacyidea_authenticator/utils/firebase_utils.dart' as _i16; -import 'package:privacyidea_authenticator/utils/network_utils.dart' as _i11; -import 'package:privacyidea_authenticator/utils/rsa_utils.dart' as _i12; +import 'package:privacyidea_authenticator/interfaces/repo/introduction_repository.dart' as _i19; +import 'package:privacyidea_authenticator/interfaces/repo/settings_repository.dart' as _i9; +import 'package:privacyidea_authenticator/interfaces/repo/token_folder_repository.dart' as _i10; +import 'package:privacyidea_authenticator/interfaces/repo/token_repository.dart' as _i6; +import 'package:privacyidea_authenticator/model/states/introduction_state.dart' as _i5; +import 'package:privacyidea_authenticator/model/states/settings_state.dart' as _i2; +import 'package:privacyidea_authenticator/model/token_folder.dart' as _i11; +import 'package:privacyidea_authenticator/model/tokens/push_token.dart' as _i16; +import 'package:privacyidea_authenticator/model/tokens/token.dart' as _i8; +import 'package:privacyidea_authenticator/utils/firebase_utils.dart' as _i17; +import 'package:privacyidea_authenticator/utils/network_utils.dart' as _i12; +import 'package:privacyidea_authenticator/utils/rsa_utils.dart' as _i13; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -79,9 +77,7 @@ class _FakeRSAPrivateKey_3 extends _i1.SmartFake implements _i4.RSAPrivateKey { ); } -class _FakeAsymmetricKeyPair_4 extends _i1.SmartFake - implements _i4.AsymmetricKeyPair { +class _FakeAsymmetricKeyPair_4 extends _i1.SmartFake implements _i4.AsymmetricKeyPair { _FakeAsymmetricKeyPair_4( Object parent, Invocation parentInvocation, @@ -91,125 +87,119 @@ class _FakeAsymmetricKeyPair_4> saveOrReplaceTokens(List<_i7.Token>? tokens) => - (super.noSuchMethod( + _i7.Future> saveOrReplaceTokens(List<_i8.Token>? tokens) => (super.noSuchMethod( Invocation.method( - #saveOrReplaceTokens, + #saveNewState, [tokens], ), - returnValue: _i6.Future>.value(<_i7.Token>[]), - returnValueForMissingStub: - _i6.Future>.value(<_i7.Token>[]), - ) as _i6.Future>); + returnValue: _i7.Future>.value(<_i8.Token>[]), + returnValueForMissingStub: _i7.Future>.value(<_i8.Token>[]), + ) as _i7.Future>); @override - _i6.Future> loadTokens() => (super.noSuchMethod( + _i7.Future> loadTokens() => (super.noSuchMethod( Invocation.method( #loadTokens, [], ), - returnValue: _i6.Future>.value(<_i7.Token>[]), - returnValueForMissingStub: - _i6.Future>.value(<_i7.Token>[]), - ) as _i6.Future>); + returnValue: _i7.Future>.value(<_i8.Token>[]), + returnValueForMissingStub: _i7.Future>.value(<_i8.Token>[]), + ) as _i7.Future>); @override - _i6.Future> deleteTokens(List<_i7.Token>? tokens) => - (super.noSuchMethod( + _i7.Future> deleteTokens(List<_i8.Token>? tokens) => (super.noSuchMethod( Invocation.method( #deleteTokens, [tokens], ), - returnValue: _i6.Future>.value(<_i7.Token>[]), - returnValueForMissingStub: - _i6.Future>.value(<_i7.Token>[]), - ) as _i6.Future>); + returnValue: _i7.Future>.value(<_i8.Token>[]), + returnValueForMissingStub: _i7.Future>.value(<_i8.Token>[]), + ) as _i7.Future>); } /// A class which mocks [SettingsRepository]. /// /// See the documentation for Mockito's code generation for more information. -class MockSettingsRepository extends _i1.Mock - implements _i8.SettingsRepository { +class MockSettingsRepository extends _i1.Mock implements _i9.SettingsRepository { @override - _i6.Future saveSettings(_i2.SettingsState? settings) => - (super.noSuchMethod( + _i7.Future saveSettings(_i2.SettingsState? settings) => (super.noSuchMethod( Invocation.method( #saveSettings, [settings], ), - returnValue: _i6.Future.value(false), - returnValueForMissingStub: _i6.Future.value(false), - ) as _i6.Future); + returnValue: _i7.Future.value(false), + returnValueForMissingStub: _i7.Future.value(false), + ) as _i7.Future); @override - _i6.Future<_i2.SettingsState> loadSettings() => (super.noSuchMethod( + _i7.Future<_i2.SettingsState> loadSettings() => (super.noSuchMethod( Invocation.method( #loadSettings, [], ), - returnValue: _i6.Future<_i2.SettingsState>.value(_FakeSettingsState_0( + returnValue: _i7.Future<_i2.SettingsState>.value(_FakeSettingsState_0( this, Invocation.method( #loadSettings, [], ), )), - returnValueForMissingStub: - _i6.Future<_i2.SettingsState>.value(_FakeSettingsState_0( + returnValueForMissingStub: _i7.Future<_i2.SettingsState>.value(_FakeSettingsState_0( this, Invocation.method( #loadSettings, [], ), )), - ) as _i6.Future<_i2.SettingsState>); + ) as _i7.Future<_i2.SettingsState>); } /// A class which mocks [TokenFolderRepository]. /// /// See the documentation for Mockito's code generation for more information. -class MockTokenFolderRepository extends _i1.Mock - implements _i9.TokenFolderRepository { +class MockTokenFolderRepository extends _i1.Mock implements _i10.TokenFolderRepository { @override - _i6.Future> saveOrReplaceFolders( - List<_i10.TokenFolder>? folders) => - (super.noSuchMethod( + _i7.Future> saveOrReplaceFolders(List<_i11.TokenFolder>? folders) => (super.noSuchMethod( Invocation.method( #saveOrReplaceFolders, [folders], ), - returnValue: - _i6.Future>.value(<_i10.TokenFolder>[]), - returnValueForMissingStub: - _i6.Future>.value(<_i10.TokenFolder>[]), - ) as _i6.Future>); + returnValue: _i7.Future>.value(<_i11.TokenFolder>[]), + returnValueForMissingStub: _i7.Future>.value(<_i11.TokenFolder>[]), + ) as _i7.Future>); @override - _i6.Future> loadFolders() => (super.noSuchMethod( + _i7.Future> loadFolders() => (super.noSuchMethod( Invocation.method( #loadFolders, [], ), - returnValue: - _i6.Future>.value(<_i10.TokenFolder>[]), - returnValueForMissingStub: - _i6.Future>.value(<_i10.TokenFolder>[]), - ) as _i6.Future>); + returnValue: _i7.Future>.value(<_i11.TokenFolder>[]), + returnValueForMissingStub: _i7.Future>.value(<_i11.TokenFolder>[]), + ) as _i7.Future>); } /// A class which mocks [PrivacyIdeaIOClient]. /// /// See the documentation for Mockito's code generation for more information. -class MockPrivacyIdeaIOClient extends _i1.Mock - implements _i11.PrivacyIdeaIOClient { +class MockPrivacyIdeaIOClient extends _i1.Mock implements _i12.PrivacyIdeaIOClient { @override - _i6.Future triggerNetworkAccessPermission({ + _i7.Future triggerNetworkAccessPermission({ required Uri? url, bool? sslVerify = true, bool? isRetry = false, @@ -224,12 +214,12 @@ class MockPrivacyIdeaIOClient extends _i1.Mock #isRetry: isRetry, }, ), - returnValue: _i6.Future.value(false), - returnValueForMissingStub: _i6.Future.value(false), - ) as _i6.Future); + returnValue: _i7.Future.value(false), + returnValueForMissingStub: _i7.Future.value(false), + ) as _i7.Future); @override - _i6.Future<_i3.Response> doPost({ + _i7.Future<_i3.Response> doPost({ required Uri? url, required Map? body, bool? sslVerify = true, @@ -244,7 +234,7 @@ class MockPrivacyIdeaIOClient extends _i1.Mock #sslVerify: sslVerify, }, ), - returnValue: _i6.Future<_i3.Response>.value(_FakeResponse_1( + returnValue: _i7.Future<_i3.Response>.value(_FakeResponse_1( this, Invocation.method( #doPost, @@ -256,8 +246,7 @@ class MockPrivacyIdeaIOClient extends _i1.Mock }, ), )), - returnValueForMissingStub: - _i6.Future<_i3.Response>.value(_FakeResponse_1( + returnValueForMissingStub: _i7.Future<_i3.Response>.value(_FakeResponse_1( this, Invocation.method( #doPost, @@ -269,10 +258,10 @@ class MockPrivacyIdeaIOClient extends _i1.Mock }, ), )), - ) as _i6.Future<_i3.Response>); + ) as _i7.Future<_i3.Response>); @override - _i6.Future<_i3.Response> doGet({ + _i7.Future<_i3.Response> doGet({ required Uri? url, required Map? parameters, bool? sslVerify = true, @@ -287,7 +276,7 @@ class MockPrivacyIdeaIOClient extends _i1.Mock #sslVerify: sslVerify, }, ), - returnValue: _i6.Future<_i3.Response>.value(_FakeResponse_1( + returnValue: _i7.Future<_i3.Response>.value(_FakeResponse_1( this, Invocation.method( #doGet, @@ -299,8 +288,7 @@ class MockPrivacyIdeaIOClient extends _i1.Mock }, ), )), - returnValueForMissingStub: - _i6.Future<_i3.Response>.value(_FakeResponse_1( + returnValueForMissingStub: _i7.Future<_i3.Response>.value(_FakeResponse_1( this, Invocation.method( #doGet, @@ -312,16 +300,15 @@ class MockPrivacyIdeaIOClient extends _i1.Mock }, ), )), - ) as _i6.Future<_i3.Response>); + ) as _i7.Future<_i3.Response>); } /// A class which mocks [RsaUtils]. /// /// See the documentation for Mockito's code generation for more information. -class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { +class MockRsaUtils extends _i1.Mock implements _i13.RsaUtils { @override - _i4.RSAPublicKey deserializeRSAPublicKeyPKCS1(String? keyStr) => - (super.noSuchMethod( + _i4.RSAPublicKey deserializeRSAPublicKeyPKCS1(String? keyStr) => (super.noSuchMethod( Invocation.method( #deserializeRSAPublicKeyPKCS1, [keyStr], @@ -343,20 +330,19 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { ) as _i4.RSAPublicKey); @override - String serializeRSAPublicKeyPKCS1(_i4.RSAPublicKey? publicKey) => - (super.noSuchMethod( + String serializeRSAPublicKeyPKCS1(_i4.RSAPublicKey? publicKey) => (super.noSuchMethod( Invocation.method( #serializeRSAPublicKeyPKCS1, [publicKey], ), - returnValue: _i13.dummyValue( + returnValue: _i14.dummyValue( this, Invocation.method( #serializeRSAPublicKeyPKCS1, [publicKey], ), ), - returnValueForMissingStub: _i13.dummyValue( + returnValueForMissingStub: _i14.dummyValue( this, Invocation.method( #serializeRSAPublicKeyPKCS1, @@ -366,8 +352,7 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { ) as String); @override - _i4.RSAPublicKey deserializeRSAPublicKeyPKCS8(String? keyStr) => - (super.noSuchMethod( + _i4.RSAPublicKey deserializeRSAPublicKeyPKCS8(String? keyStr) => (super.noSuchMethod( Invocation.method( #deserializeRSAPublicKeyPKCS8, [keyStr], @@ -389,20 +374,19 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { ) as _i4.RSAPublicKey); @override - String serializeRSAPublicKeyPKCS8(_i4.RSAPublicKey? key) => - (super.noSuchMethod( + String serializeRSAPublicKeyPKCS8(_i4.RSAPublicKey? key) => (super.noSuchMethod( Invocation.method( #serializeRSAPublicKeyPKCS8, [key], ), - returnValue: _i13.dummyValue( + returnValue: _i14.dummyValue( this, Invocation.method( #serializeRSAPublicKeyPKCS8, [key], ), ), - returnValueForMissingStub: _i13.dummyValue( + returnValueForMissingStub: _i14.dummyValue( this, Invocation.method( #serializeRSAPublicKeyPKCS8, @@ -412,20 +396,19 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { ) as String); @override - String serializeRSAPrivateKeyPKCS1(_i4.RSAPrivateKey? key) => - (super.noSuchMethod( + String serializeRSAPrivateKeyPKCS1(_i4.RSAPrivateKey? key) => (super.noSuchMethod( Invocation.method( #serializeRSAPrivateKeyPKCS1, [key], ), - returnValue: _i13.dummyValue( + returnValue: _i14.dummyValue( this, Invocation.method( #serializeRSAPrivateKeyPKCS1, [key], ), ), - returnValueForMissingStub: _i13.dummyValue( + returnValueForMissingStub: _i14.dummyValue( this, Invocation.method( #serializeRSAPrivateKeyPKCS1, @@ -435,8 +418,7 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { ) as String); @override - _i4.RSAPrivateKey deserializeRSAPrivateKeyPKCS1(String? keyStr) => - (super.noSuchMethod( + _i4.RSAPrivateKey deserializeRSAPrivateKeyPKCS1(String? keyStr) => (super.noSuchMethod( Invocation.method( #deserializeRSAPrivateKeyPKCS1, [keyStr], @@ -460,8 +442,8 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { @override bool verifyRSASignature( _i4.RSAPublicKey? publicKey, - _i14.Uint8List? signedMessage, - _i14.Uint8List? signature, + _i15.Uint8List? signedMessage, + _i15.Uint8List? signature, ) => (super.noSuchMethod( Invocation.method( @@ -477,8 +459,8 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { ) as bool); @override - _i6.Future trySignWithToken( - _i15.PushToken? token, + _i7.Future trySignWithToken( + _i16.PushToken? token, String? message, ) => (super.noSuchMethod( @@ -489,44 +471,37 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { message, ], ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); @override - _i6.Future<_i4.AsymmetricKeyPair<_i4.RSAPublicKey, _i4.RSAPrivateKey>> - generateRSAKeyPair() => (super.noSuchMethod( - Invocation.method( - #generateRSAKeyPair, - [], - ), - returnValue: _i6.Future< - _i4.AsymmetricKeyPair<_i4.RSAPublicKey, - _i4.RSAPrivateKey>>.value( - _FakeAsymmetricKeyPair_4<_i4.RSAPublicKey, _i4.RSAPrivateKey>( - this, - Invocation.method( - #generateRSAKeyPair, - [], - ), - )), - returnValueForMissingStub: _i6.Future< - _i4.AsymmetricKeyPair<_i4.RSAPublicKey, - _i4.RSAPrivateKey>>.value( - _FakeAsymmetricKeyPair_4<_i4.RSAPublicKey, _i4.RSAPrivateKey>( - this, - Invocation.method( - #generateRSAKeyPair, - [], - ), - )), - ) as _i6.Future< - _i4.AsymmetricKeyPair<_i4.RSAPublicKey, _i4.RSAPrivateKey>>); + _i7.Future<_i4.AsymmetricKeyPair<_i4.RSAPublicKey, _i4.RSAPrivateKey>> generateRSAKeyPair() => (super.noSuchMethod( + Invocation.method( + #generateRSAKeyPair, + [], + ), + returnValue: _i7.Future<_i4.AsymmetricKeyPair<_i4.RSAPublicKey, _i4.RSAPrivateKey>>.value(_FakeAsymmetricKeyPair_4<_i4.RSAPublicKey, _i4.RSAPrivateKey>( + this, + Invocation.method( + #generateRSAKeyPair, + [], + ), + )), + returnValueForMissingStub: + _i7.Future<_i4.AsymmetricKeyPair<_i4.RSAPublicKey, _i4.RSAPrivateKey>>.value(_FakeAsymmetricKeyPair_4<_i4.RSAPublicKey, _i4.RSAPrivateKey>( + this, + Invocation.method( + #generateRSAKeyPair, + [], + ), + )), + ) as _i7.Future<_i4.AsymmetricKeyPair<_i4.RSAPublicKey, _i4.RSAPrivateKey>>); @override String createBase32Signature( _i4.RSAPrivateKey? privateKey, - _i14.Uint8List? dataToSign, + _i15.Uint8List? dataToSign, ) => (super.noSuchMethod( Invocation.method( @@ -536,7 +511,7 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { dataToSign, ], ), - returnValue: _i13.dummyValue( + returnValue: _i14.dummyValue( this, Invocation.method( #createBase32Signature, @@ -546,7 +521,7 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { ], ), ), - returnValueForMissingStub: _i13.dummyValue( + returnValueForMissingStub: _i14.dummyValue( this, Invocation.method( #createBase32Signature, @@ -559,9 +534,9 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { ) as String); @override - _i14.Uint8List createRSASignature( + _i15.Uint8List createRSASignature( _i4.RSAPrivateKey? privateKey, - _i14.Uint8List? dataToSign, + _i15.Uint8List? dataToSign, ) => (super.noSuchMethod( Invocation.method( @@ -571,19 +546,19 @@ class MockRsaUtils extends _i1.Mock implements _i12.RsaUtils { dataToSign, ], ), - returnValue: _i14.Uint8List(0), - returnValueForMissingStub: _i14.Uint8List(0), - ) as _i14.Uint8List); + returnValue: _i15.Uint8List(0), + returnValueForMissingStub: _i15.Uint8List(0), + ) as _i15.Uint8List); } /// A class which mocks [FirebaseUtils]. /// /// See the documentation for Mockito's code generation for more information. -class MockFirebaseUtils extends _i1.Mock implements _i16.FirebaseUtils { +class MockFirebaseUtils extends _i1.Mock implements _i17.FirebaseUtils { @override - _i6.Future initFirebase({ - required _i6.Future Function(_i17.RemoteMessage)? foregroundHandler, - required _i6.Future Function(_i17.RemoteMessage)? backgroundHandler, + _i7.Future initFirebase({ + required _i7.Future Function(_i18.RemoteMessage)? foregroundHandler, + required _i7.Future Function(_i18.RemoteMessage)? backgroundHandler, required dynamic Function(String?)? updateFirebaseToken, }) => (super.noSuchMethod( @@ -596,17 +571,54 @@ class MockFirebaseUtils extends _i1.Mock implements _i16.FirebaseUtils { #updateFirebaseToken: updateFirebaseToken, }, ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); @override - _i6.Future getFBToken() => (super.noSuchMethod( + _i7.Future getFBToken() => (super.noSuchMethod( Invocation.method( #getFBToken, [], ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); +} + +/// A class which mocks [IntroductionRepository]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIntroductionRepository extends _i1.Mock implements _i19.IntroductionRepository { + @override + _i7.Future saveCompletedIntroductions(_i5.IntroductionState? introductions) => (super.noSuchMethod( + Invocation.method( + #saveCompletedIntroductions, + [introductions], + ), + returnValue: _i7.Future.value(false), + returnValueForMissingStub: _i7.Future.value(false), + ) as _i7.Future); + + @override + _i7.Future<_i5.IntroductionState> loadCompletedIntroductions() => (super.noSuchMethod( + Invocation.method( + #loadCompletedIntroductions, + [], + ), + returnValue: _i7.Future<_i5.IntroductionState>.value(_FakeIntroductionState_5( + this, + Invocation.method( + #loadCompletedIntroductions, + [], + ), + )), + returnValueForMissingStub: _i7.Future<_i5.IntroductionState>.value(_FakeIntroductionState_5( + this, + Invocation.method( + #loadCompletedIntroductions, + [], + ), + )), + ) as _i7.Future<_i5.IntroductionState>); } diff --git a/test/unit_test/model/states_test/introduction_state_test.dart b/test/unit_test/model/states_test/introduction_state_test.dart index e69de29bb..c78aaeda4 100644 --- a/test/unit_test/model/states_test/introduction_state_test.dart +++ b/test/unit_test/model/states_test/introduction_state_test.dart @@ -0,0 +1,25 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:privacyidea_authenticator/model/enums/introduction.dart'; +import 'package:privacyidea_authenticator/model/states/introduction_state.dart'; + +void main() { + group('IntroductionState', () { + test('withCompletedIntroduction add introduction', () { + const introductionState = IntroductionState(completedIntroductions: {Introduction.addFolder}); + final updatedState = introductionState.withCompletedIntroduction(Introduction.addTokenManually); + expect(updatedState.completedIntroductions, {Introduction.addFolder, Introduction.addTokenManually}); + }); + + test('withoutCompletedIntroduction remove introduction', () { + const introductionState = IntroductionState(completedIntroductions: {Introduction.addFolder, Introduction.addTokenManually}); + final updatedState = introductionState.withoutCompletedIntroduction(Introduction.addTokenManually); + expect(updatedState.completedIntroductions, {Introduction.addFolder}); + }); + + test('withoutCompletedIntroduction add duplicate introduction', () { + const introductionState = IntroductionState(completedIntroductions: {Introduction.addFolder, Introduction.addTokenManually}); + final updatedState = introductionState.withCompletedIntroduction(Introduction.addTokenManually); + expect(updatedState.completedIntroductions, {Introduction.addFolder, Introduction.addTokenManually}); + }); + }); +} diff --git a/test/unit_test/model/token_test/push_token_test.dart b/test/unit_test/model/token_test/push_token_test.dart index 5a34120bf..f5e778ddf 100644 --- a/test/unit_test/model/token_test/push_token_test.dart +++ b/test/unit_test/model/token_test/push_token_test.dart @@ -247,11 +247,13 @@ void _testPushToken() { "label": "label", "issuer": "issuer", "id": "id", - "isLocked": true, "pin": true, + "isLocked": true, + "isHidden": false, "tokenImage": "example.png", "folderId": 0, "sortIndex": 0, + "origin": null, "type": "PIPUSH", "expirationDate": "2017-09-07T17:30:00.000", "serial": "serial", diff --git a/test/unit_test/state_notifiers/token_notifier_test.dart b/test/unit_test/state_notifiers/token_notifier_test.dart index 4edb56692..bf1bc2db4 100644 --- a/test/unit_test/state_notifiers/token_notifier_test.dart +++ b/test/unit_test/state_notifiers/token_notifier_test.dart @@ -16,6 +16,7 @@ import 'package:privacyidea_authenticator/model/tokens/push_token.dart'; import 'package:privacyidea_authenticator/model/tokens/token.dart'; import 'package:privacyidea_authenticator/state_notifiers/token_notifier.dart'; import 'package:privacyidea_authenticator/utils/firebase_utils.dart'; +import 'package:privacyidea_authenticator/utils/logger.dart'; import 'package:privacyidea_authenticator/utils/network_utils.dart'; import 'package:privacyidea_authenticator/utils/rsa_utils.dart'; @@ -60,7 +61,7 @@ void _testTokenNotifier() { (ref) => TokenNotifier(repository: mockRepo), ); final notifier = container.read(testProvider.notifier); - expect(await notifier.refreshTokens(), true); + expect((await notifier.loadStateFromRepo())?.tokens, after); final state = container.read(testProvider); expect(state, isNotNull); expect(state.tokens, after); @@ -105,7 +106,7 @@ void _testTokenNotifier() { final state = container.read(testProvider); expect(state, isNotNull); expect(state.tokens, after); - verify(mockRepo.saveOrReplaceTokens([after.first])).called(1); + verify(mockRepo.saveOrReplaceTokens(after)).called(1); }); test('removeToken', () async { final container = ProviderContainer(); @@ -216,20 +217,12 @@ void _testTokenNotifier() { ]; when(mockRepo.loadTokens()).thenAnswer((_) async => before); when(mockRepo.saveOrReplaceTokens(any)).thenAnswer((_) async => []); - // when(mockQrParser.parseUriToMap('otpAuthString')).thenReturn({ - // URI_LABEL: 'label2', - // URI_ISSUER: 'issuer2', - // URI_ALGORITHM: 'SHA256', - // URI_DIGITS: 6, - // URI_SECRET: Uint8List.fromList([73, 65, 63, 72, 65, 74, 32]), - // URI_TYPE: TokenTypes.HOTP.asString, - // }); - // when(mockQrParser._is2StepURI(any)).thenReturn(false); + final testProvider = StateNotifierProvider( (ref) => TokenNotifier(repository: mockRepo), ); final notifier = container.read(testProvider.notifier); - await notifier.handleQrCode('otpAuthString'); + await notifier.handleQrCode('otpauth://totp/issuer2:label2?secret=secret2&issuer=issuer2&algorithm=SHA256&digits=6&period=30'); final state = container.read(testProvider); expect(state, isNotNull); after.last = after.last.copyWith(id: state.tokens.last.id); @@ -299,7 +292,7 @@ void _testTokenNotifier() { )).thenAnswer( (_) => Future.value( Response( - '{"detail": {"public_key": "MIICCgKCAgEAomCYODF47vz/axztjlmEcepqZPC8NNhXTlPu/FPGJ+qIOq+swTiEYgmv8DYIAslqLy3EHa7JUouSlE3f1l4OUcqZvPGgEP5Cpbjnaddy6u4Pt37YLDtlhX7nnd+VZnDLxXxqQ62e1CEOJVjKWq1x2Bq2GPcQz0fwWfGjNH7PtN+F00i3NiN0FPigOD4p7Bcru1ihWToQMobzf/p1945Yu0fwfpwUhHn0cfG5uKUrXl4T24s0b92MA8CmxYKKlenEQu9EezljeH2PJ0h1kfv58xjAEVEdwjCb8jzHwXomzJWUqZHt0BexavR+sUQNyk8r5OdX0fgOo+4W3/H+b/0Ktn47Frn827pYB8c2AX8lqxFocP6lj62hjCfKWss0rgqQBegTd9trCuN2iiw/Dj1HLFzK2Z8JwGDrQni1F8nyevaaZOuZI3I4DAFJzYKcP/zDvkNs6qpa+P1kzg50ml3m0RONGIHrzcSeo3aVeaMMdHXKhB5dqrig6Sjblqt2hwdPAWQPOiq9pTAXZIJmXI0UJb3bfWKlPIUmiZPRs+xYom+aZ9VEBTLdcxGC6puAJyUsjoXBJTJqH7O8g/pWA02UfPALEcuDAVQOSJbahodkWmrBg8jIMnjNOkN1t9hxHbg5XSWidgei4D/MJp4xH9w0eHyVZSnVTY5Iah0GkCVQFVsCAwEAAQ==", "rollout_state": "enrolled", "serial": "PIPU0006BF18", "threadid": 140024860083968}, "id": 1, "jsonrpc": "2.0", "result": {"status": true, "value": true}, "time": 1701091444.6211884, "version": "privacyIDEA 3.9.dev3", "versionnumber": "3.9.dev3", "signature": "rsa_sha256_pss:c137b543b0df817ebd89ff53c5924c94f916c2bfebbe03ceb14e806ffdb46deb00fd336c83f3e0fb06ffbdf4926e83b5440f7f117498341608d644e4c1f2bbf9319eb59b98d5485c42b40325c9f29427cc8ae67728e486db247be0510a92f74936ea57436ecbe5304bcc50fcb624c3bde8e3039419592e9fbe8c0cb85431c2931ea8d6a6369fccf7e4c15c9cfaea896d8ec7896811545083bd6d3f5416e7d54b43f1f4752bf2a57c2b12a139fe217d1eec1292b071b9c6cef31e5f6eb957c7ad2a1d3bd105a74c80f961f5e307393824b8767807116a8573448f45f6cc112317105fb4e9e294f1a99faaf78b2f902ea1553cf5e428bfa98041c74cc23302df6f"}', + '{"detail": {"public_key": "$publicServerKeyString", "rollout_state": "enrolled", "serial": "PIPU0006BF18", "threadid": 140024860083968}, "id": 1, "jsonrpc": "2.0", "result": {"status": true, "value": true}, "time": 1701091444.6211884, "version": "privacyIDEA 3.9.dev3", "versionnumber": "3.9.dev3", "signature": "rsa_sha256_pss:c137b543b0df817ebd89ff53c5924c94f916c2bfebbe03ceb14e806ffdb46deb00fd336c83f3e0fb06ffbdf4926e83b5440f7f117498341608d644e4c1f2bbf9319eb59b98d5485c42b40325c9f29427cc8ae67728e486db247be0510a92f74936ea57436ecbe5304bcc50fcb624c3bde8e3039419592e9fbe8c0cb85431c2931ea8d6a6369fccf7e4c15c9cfaea896d8ec7896811545083bd6d3f5416e7d54b43f1f4752bf2a57c2b12a139fe217d1eec1292b071b9c6cef31e5f6eb957c7ad2a1d3bd105a74c80f961f5e307393824b8767807116a8573448f45f6cc112317105fb4e9e294f1a99faaf78b2f902ea1553cf5e428bfa98041c74cc23302df6f"}', 200, ), ), @@ -443,8 +436,11 @@ void _testTokenNotifier() { firebaseUtils: mockFirebaseUtils, ), ); + final notifier = container.read(testProvider.notifier); + Logger.info('before rolloutPushToken'); expect(await notifier.rolloutPushToken(before.first), true); + Logger.info('after rolloutPushToken'); final state = container.read(testProvider); expect(state, isNotNull); expect(state.tokens, after); @@ -463,10 +459,12 @@ void _testTokenNotifier() { final before = [ HOTPToken(label: 'label', issuer: 'issuer', id: 'id', algorithm: Algorithms.SHA1, digits: 6, secret: 'secret'), ]; - when(mockRepo.loadTokens()).thenAnswer((_) async => before); + when(mockRepo.loadTokens()).thenAnswer((_) => Future.value(before)); final notifier = TokenNotifier(repository: mockRepo); - final result = await notifier.loadFromRepo(); - expect(result, true); + Logger.info('before loadFromRepo'); + final newState = await notifier.loadStateFromRepo(); + Logger.info('after loadFromRepo'); + expect(newState?.tokens, before); expect(notifier.state.tokens, before); }); }); diff --git a/test/unit_test/state_notifiers/token_notifier_test.mocks.dart b/test/unit_test/state_notifiers/token_notifier_test.mocks.dart index 772d02fba..c2d5f5f5c 100644 --- a/test/unit_test/state_notifiers/token_notifier_test.mocks.dart +++ b/test/unit_test/state_notifiers/token_notifier_test.mocks.dart @@ -12,8 +12,7 @@ import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i8; import 'package:pi_authenticator_legacy/pi_authenticator_legacy.dart' as _i14; import 'package:pointycastle/export.dart' as _i2; -import 'package:privacyidea_authenticator/interfaces/repo/token_repository.dart' - as _i4; +import 'package:privacyidea_authenticator/interfaces/repo/token_repository.dart' as _i4; import 'package:privacyidea_authenticator/model/tokens/push_token.dart' as _i10; import 'package:privacyidea_authenticator/model/tokens/token.dart' as _i6; import 'package:privacyidea_authenticator/utils/firebase_utils.dart' as _i12; @@ -53,9 +52,7 @@ class _FakeRSAPrivateKey_1 extends _i1.SmartFake implements _i2.RSAPrivateKey { ); } -class _FakeAsymmetricKeyPair_2 extends _i1.SmartFake - implements _i2.AsymmetricKeyPair { +class _FakeAsymmetricKeyPair_2 extends _i1.SmartFake implements _i2.AsymmetricKeyPair { _FakeAsymmetricKeyPair_2( Object parent, Invocation parentInvocation, @@ -84,10 +81,9 @@ class MockTokenRepository extends _i1.Mock implements _i4.TokenRepository { } @override - _i5.Future> saveOrReplaceTokens(List<_i6.Token>? tokens) => - (super.noSuchMethod( + _i5.Future> saveOrReplaceTokens(List<_i6.Token>? tokens) => (super.noSuchMethod( Invocation.method( - #saveOrReplaceTokens, + #saveNewState, [tokens], ), returnValue: _i5.Future>.value(<_i6.Token>[]), @@ -103,8 +99,7 @@ class MockTokenRepository extends _i1.Mock implements _i4.TokenRepository { ) as _i5.Future>); @override - _i5.Future> deleteTokens(List<_i6.Token>? tokens) => - (super.noSuchMethod( + _i5.Future> deleteTokens(List<_i6.Token>? tokens) => (super.noSuchMethod( Invocation.method( #deleteTokens, [tokens], @@ -122,8 +117,7 @@ class MockRsaUtils extends _i1.Mock implements _i7.RsaUtils { } @override - _i2.RSAPublicKey deserializeRSAPublicKeyPKCS1(String? keyStr) => - (super.noSuchMethod( + _i2.RSAPublicKey deserializeRSAPublicKeyPKCS1(String? keyStr) => (super.noSuchMethod( Invocation.method( #deserializeRSAPublicKeyPKCS1, [keyStr], @@ -138,8 +132,7 @@ class MockRsaUtils extends _i1.Mock implements _i7.RsaUtils { ) as _i2.RSAPublicKey); @override - String serializeRSAPublicKeyPKCS1(_i2.RSAPublicKey? publicKey) => - (super.noSuchMethod( + String serializeRSAPublicKeyPKCS1(_i2.RSAPublicKey? publicKey) => (super.noSuchMethod( Invocation.method( #serializeRSAPublicKeyPKCS1, [publicKey], @@ -154,8 +147,7 @@ class MockRsaUtils extends _i1.Mock implements _i7.RsaUtils { ) as String); @override - _i2.RSAPublicKey deserializeRSAPublicKeyPKCS8(String? keyStr) => - (super.noSuchMethod( + _i2.RSAPublicKey deserializeRSAPublicKeyPKCS8(String? keyStr) => (super.noSuchMethod( Invocation.method( #deserializeRSAPublicKeyPKCS8, [keyStr], @@ -170,8 +162,7 @@ class MockRsaUtils extends _i1.Mock implements _i7.RsaUtils { ) as _i2.RSAPublicKey); @override - String serializeRSAPublicKeyPKCS8(_i2.RSAPublicKey? key) => - (super.noSuchMethod( + String serializeRSAPublicKeyPKCS8(_i2.RSAPublicKey? key) => (super.noSuchMethod( Invocation.method( #serializeRSAPublicKeyPKCS8, [key], @@ -186,8 +177,7 @@ class MockRsaUtils extends _i1.Mock implements _i7.RsaUtils { ) as String); @override - String serializeRSAPrivateKeyPKCS1(_i2.RSAPrivateKey? key) => - (super.noSuchMethod( + String serializeRSAPrivateKeyPKCS1(_i2.RSAPrivateKey? key) => (super.noSuchMethod( Invocation.method( #serializeRSAPrivateKeyPKCS1, [key], @@ -202,8 +192,7 @@ class MockRsaUtils extends _i1.Mock implements _i7.RsaUtils { ) as String); @override - _i2.RSAPrivateKey deserializeRSAPrivateKeyPKCS1(String? keyStr) => - (super.noSuchMethod( + _i2.RSAPrivateKey deserializeRSAPrivateKeyPKCS1(String? keyStr) => (super.noSuchMethod( Invocation.method( #deserializeRSAPrivateKeyPKCS1, [keyStr], @@ -252,24 +241,19 @@ class MockRsaUtils extends _i1.Mock implements _i7.RsaUtils { ) as _i5.Future); @override - _i5.Future<_i2.AsymmetricKeyPair<_i2.RSAPublicKey, _i2.RSAPrivateKey>> - generateRSAKeyPair() => (super.noSuchMethod( - Invocation.method( - #generateRSAKeyPair, - [], - ), - returnValue: _i5.Future< - _i2.AsymmetricKeyPair<_i2.RSAPublicKey, - _i2.RSAPrivateKey>>.value( - _FakeAsymmetricKeyPair_2<_i2.RSAPublicKey, _i2.RSAPrivateKey>( - this, - Invocation.method( - #generateRSAKeyPair, - [], - ), - )), - ) as _i5.Future< - _i2.AsymmetricKeyPair<_i2.RSAPublicKey, _i2.RSAPrivateKey>>); + _i5.Future<_i2.AsymmetricKeyPair<_i2.RSAPublicKey, _i2.RSAPrivateKey>> generateRSAKeyPair() => (super.noSuchMethod( + Invocation.method( + #generateRSAKeyPair, + [], + ), + returnValue: _i5.Future<_i2.AsymmetricKeyPair<_i2.RSAPublicKey, _i2.RSAPrivateKey>>.value(_FakeAsymmetricKeyPair_2<_i2.RSAPublicKey, _i2.RSAPrivateKey>( + this, + Invocation.method( + #generateRSAKeyPair, + [], + ), + )), + ) as _i5.Future<_i2.AsymmetricKeyPair<_i2.RSAPublicKey, _i2.RSAPrivateKey>>); @override String createBase32Signature( @@ -316,8 +300,7 @@ class MockRsaUtils extends _i1.Mock implements _i7.RsaUtils { /// A class which mocks [PrivacyIdeaIOClient]. /// /// See the documentation for Mockito's code generation for more information. -class MockPrivacyIdeaIOClient extends _i1.Mock - implements _i11.PrivacyIdeaIOClient { +class MockPrivacyIdeaIOClient extends _i1.Mock implements _i11.PrivacyIdeaIOClient { MockPrivacyIdeaIOClient() { _i1.throwOnMissingStub(this); } diff --git a/test/unit_test/utils/parser/qr_parser_test.dart b/test/unit_test/utils/parser/qr_parser_test.dart deleted file mode 100644 index 813a4c6d7..000000000 --- a/test/unit_test/utils/parser/qr_parser_test.dart +++ /dev/null @@ -1,228 +0,0 @@ -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:privacyidea_authenticator/model/enums/encodings.dart'; -// import 'package:privacyidea_authenticator/utils/crypto_utils.dart'; -// import 'package:privacyidea_authenticator/utils/identifiers.dart'; -// import 'package:privacyidea_authenticator/utils/qr_parser.dart'; - -// void main() { -// _testParsingLabelAndIssuer(); -// _testParseOtpAuth(); -// } - -// void _testParsingLabelAndIssuer() { -// group('Parsing Label and Issuer', () { -// QrParser qrParser = const QrParser(); -// test('Test parse issuer from param', () { -// String uriWithoutIssuerAndLabel = 'otpauth://totp/?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ' -// '&algorithm=SHA512&digits=8&period=60'; -// Map map = qrParser.parseUriToMap(uriWithoutIssuerAndLabel); -// expect(map[URI_LABEL], ''); -// expect(map[URI_ISSUER], ''); -// }); - -// test('Test parse issuer from param', () { -// String uriWithIssuerParam = 'otpauth://totp/alice@google.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ' -// '&issuer=ACME%20Co&algorithm=SHA512&digits=8&period=60'; -// Map map = qrParser.parseUriToMap(uriWithIssuerParam); -// expect(map[URI_LABEL], 'alice@google.com'); -// expect(map[URI_ISSUER], 'ACME Co'); -// }); - -// test('Test parse issuer from label', () { -// String uriWithIssuer = 'otpauth://totp/Example:alice@google.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ' -// '&algorithm=SHA512&digits=8&period=60'; -// Map map = qrParser.parseUriToMap(uriWithIssuer); -// expect(map[URI_LABEL], 'alice@google.com'); -// expect(map[URI_ISSUER], 'Example'); -// }); - -// test('Test parse issuer from label with uri encoding', () { -// String uriWithIssuerAndUriEncoding = 'otpauth://totp/Example%3Aalice@google.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ' -// '&algorithm=SHA512&digits=8&period=60'; -// Map map = qrParser.parseUriToMap(uriWithIssuerAndUriEncoding); -// expect(map[URI_LABEL], 'alice@google.com'); -// expect(map[URI_ISSUER], 'Example'); -// }); - -// test('Test parse issuer from param and label', () { -// String uriWithIssuerParamAndIssuer = 'otpauth://totp/Example:alice@google.com?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ' -// '&issuer=ACME%20Co&algorithm=SHA512&digits=8&period=60'; -// Map map = qrParser.parseUriToMap(uriWithIssuerParamAndIssuer); -// expect(map[URI_LABEL], 'alice@google.com'); -// expect(map[URI_ISSUER], 'Example'); -// }); -// }); -// } - -// void _testParseOtpAuth() { -// group('Parse TOTP uri', () { -// const qrParser = QrParser(); -// test('Test with wrong uri schema', () { -// expect( -// () => qrParser.parseUriToMap('http://totp/ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&algorithm=SHA1&digits=6&period=30'), -// throwsA(const TypeMatcher())); -// }); - -// test('Test with unknown type', () { -// expect( -// () => qrParser.parseUriToMap('otpauth://asdf/ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&algorithm=SHA1&digits=6&period=30'), -// throwsA(const TypeMatcher())); -// }); - -// test('Test with missing type', () { -// expect( -// () => qrParser.parseUriToMap('otpauth:///ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&algorithm=SHA1&digits=6&period=30'), -// throwsA(const TypeMatcher())); -// }); - -// test('Test missing algorithm', () { -// Map map = qrParser.parseUriToMap('otpauth://totp/ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&digits=6&period=30'); -// expect(map[URI_ALGORITHM], 'SHA1'); // This is the default value -// }); - -// test('Test unknown algorithm', () { -// expect( -// () => qrParser.parseUriToMap('otpauth://totp/ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&algorithm=BubbleSort&digits=6&period=30'), -// throwsA(const TypeMatcher())); -// }); - -// test('Test missing digits', () { -// Map map = qrParser.parseUriToMap('otpauth://totp/ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&period=30'); -// expect(map[URI_DIGITS], 6); // This is the default value -// }); - -// // At least the library used to calculate otp values does not support other number of digits. -// test('Test invalid number of digits', () { -// expect( -// () => qrParser.parseUriToMap('otpauth://totp/ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&algorithm=SHA1&digits=66&period=30'), -// throwsA(const TypeMatcher())); -// }); - -// test('Test invalid characters for digits', () { -// expect( -// () => qrParser.parseUriToMap('otpauth://totp/ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&algorithm=SHA1&digits=aA&period=30'), -// throwsA(const TypeMatcher())); -// }); - -// test('Test missing secret', () { -// expect( -// () => qrParser.parseUriToMap('otpauth://totp/ACME%20Co:john@example.com?' -// 'issuer=ACME%20Co&algorithm=SHA1&digits=6&period=30'), -// throwsA(const TypeMatcher())); -// }); - -// test('Test invalid secret', () { -// expect( -// () => qrParser.parseUriToMap('otpauth://totp/ACME%20Co:john@example.com?' -// 'secret=ÖÖ&issuer=ACME%20Co&algorithm=SHA1&digits=6' -// '&period=30'), -// throwsA(const TypeMatcher())); -// }); - -// // TOTP specific -// test('Test missing period', () { -// Map map = qrParser.parseUriToMap('otpauth://totp/ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&algorithm=SHA1&digits=6'); -// expect(map[URI_PERIOD], 30); -// }); - -// test('Test invalid characters for period', () { -// expect( -// () => qrParser.parseUriToMap('otpauth://totp/ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&algorithm=SHA1&digits=6&period=aa'), -// throwsA(const TypeMatcher())); -// }); - -// test('Test longer values for period', () { -// Map map = qrParser.parseUriToMap('otpauth://totp/ACME%20Co:john@example.com?' -// 'secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ&issuer=ACME%20Co' -// '&algorithm=SHA1&digits=6&period=124432'); - -// expect(map[URI_PERIOD], 124432); -// }); - -// test('Test valid totp uri', () { -// Map map = qrParser.parseUriToMap('otpauth://totp/Kitchen?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ' -// '&issuer=ACME%20Co&algorithm=SHA512&digits=8&period=60'); -// expect(map[URI_LABEL], 'Kitchen'); -// expect(map[URI_ALGORITHM], 'SHA512'); -// expect(map[URI_DIGITS], 8); -// expect(map[URI_SECRET], decodeSecretToUint8('HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ', Encodings.base32)); -// expect(map[URI_PERIOD], 60); -// }); -// }); -// group('Parse HOTP uri', () { -// const qrParser = QrParser(); -// // HOTP specific -// test('Test with missing counter', () { -// expect( -// () => qrParser.parseUriToMap('otpauth://hotp/Kitchen?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ' -// '&issuer=ACME%20Co&algorithm=SHA256&digits=8'), -// throwsA(const TypeMatcher())); -// }); - -// test('Test with invalid counter', () { -// expect( -// () => qrParser.parseUriToMap('otpauth://hotp/Kitchen?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ' -// '&issuer=ACME%20Co&algorithm=SHA256&digits=8&counter=aa'), -// throwsA(const TypeMatcher())); -// }); - -// test('Test valid hotp uri', () { -// Map map = qrParser.parseUriToMap('otpauth://hotp/Kitchen?secret=HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ' -// '&issuer=ACME%20Co&algorithm=SHA256&digits=8&counter=5'); -// expect(map[URI_LABEL], 'Kitchen'); -// expect(map[URI_ALGORITHM], 'SHA256'); -// expect(map[URI_DIGITS], 8); -// expect(map[URI_SECRET], decodeSecretToUint8('HXDMVJECJJWSRB3HWIZR4IFUGFTMXBOZ', Encodings.base32)); -// expect(map[URI_COUNTER], 5); -// }); -// }); -// group('2 Step Rollout', () { -// const qrParser = QrParser(); -// test('is2StepURI', () { -// expect( -// qrParser._is2StepURI(Uri.parse( -// 'otpauth://hotp/OATH0001F662?secret=HDOMWJ5GEQQA6RR34RAP55QBVCX3E2RE&counter=1&digits=6&issuer=privacyIDEA&2step_salt=8&2step_output=20')), -// true, -// ); -// expect( -// qrParser._is2StepURI(Uri.parse( -// 'otpauth://hotp/OATH0001F662?secret=HDOMWJ5GEQQA6RR34RAP55QBVCX3E2RE&counter=1&digits=6&issuer=privacyIDEA&2step_salt=8&2step_difficulty=10000')), -// true, -// ); -// expect( -// qrParser._is2StepURI(Uri.parse( -// 'otpauth://hotp/OATH0001F662?secret=HDOMWJ5GEQQA6RR34RAP55QBVCX3E2RE&counter=1&digits=6&issuer=privacyIDEA&2step_output=20&2step_difficulty=10000')), -// true); -// expect( -// qrParser -// ._is2StepURI(Uri.parse('otpauth://hotp/OATH0001F662?secret=HDOMWJ5GEQQA6RR34RAP55QBVCX3E2RE&counter=1&digits=6&issuer=privacyIDEA&2step_salt=8')), -// true, -// ); - -// expect( -// qrParser._is2StepURI(Uri.parse('otpauth://hotp/OATH0001F662?secret=HDOMWJ5GEQQA6RR34RAP55QBVCX3E2RE&counter=1&digits=6&issuer=privacyIDEA')), -// false, -// ); -// }); -// }); -// }