diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 3d8ddc4eb3..39d3c5fe4f 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -119,6 +119,7 @@ 206F0DBAB6AF042CA1FF2C0D /* SettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */; }; 208C19811613F9A10F8A7B75 /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */; }; 20C16A3F718802B0E4A19C83 /* URLComponentsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76310030C831D4610A705603 /* URLComponentsTests.swift */; }; + 21813AF91CFC6F3E3896DB53 /* AppLockSetupBiometricsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10F130DF775CE6BC51A4E392 /* AppLockSetupBiometricsScreenModels.swift */; }; 2185C1F6724C78FFF355D6FA /* WelcomeScreenScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB10FA6570DD08B3966C159 /* WelcomeScreenScreenUITests.swift */; }; 21AFEFB8CEFE56A3811A1F5B /* VoiceMessageCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 283974987DA7EC61D2AB57D9 /* VoiceMessageCacheTests.swift */; }; 21BF2B7CEDFE3CA67C5355AD /* test_image.png in Resources */ = {isa = PBXBuildFile; fileRef = C733D11B421CFE3A657EF230 /* test_image.png */; }; @@ -201,6 +202,7 @@ 36CD6E11B37396E14F032CB6 /* WysiwygComposer in Frameworks */ = {isa = PBXBuildFile; productRef = CA07D57389DACE18AEB6A5E2 /* WysiwygComposer */; }; 370AF5BFCD4384DD455479B6 /* ElementCallWidgetDriverProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */; }; 377980ABF16525114E72DDE2 /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = 2B9ACE4FCACB5A8812154424 /* Version */; }; + 37906355E207DB5703754675 /* AppLockSetupBiometricsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F893F4A111CB7BA5C96949 /* AppLockSetupBiometricsScreenViewModel.swift */; }; 37D789F24199B32E3FD1AA7B /* FileRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 216F0DDC98F2A2C162D09C28 /* FileRoomTimelineItemContent.swift */; }; 383055C6ABE5BE058CEE1DDB /* WelcomeScreenScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FE5EF0AFFE360C66420AAE /* WelcomeScreenScreenCoordinator.swift */; }; 38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1D2CBAEA5D0BD00B90D1B /* BindableState.swift */; }; @@ -249,6 +251,7 @@ 44121202B4A260C98BF615A7 /* RoomMembersListScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B7A755E985FA14469E86B2 /* RoomMembersListScreenUITests.swift */; }; 44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC4F10BDD56FA77FEC742333 /* VoiceMessageMediaManagerTests.swift */; }; 44F0E1B576C7599DF8022071 /* SwiftOGG in Frameworks */ = {isa = PBXBuildFile; productRef = 391D11F92DFC91666AA1503F /* SwiftOGG */; }; + 454F8DDC4442C0DE54094902 /* LABiometryType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F219838588C62198E726E3 /* LABiometryType.swift */; }; 4557192F5B15A8D9BB920232 /* AdvancedSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E492690C8B27A892C194CC4 /* AdvancedSettingsScreenCoordinator.swift */; }; 46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; }; 4681820102DAC8BA586357D4 /* VoiceMessageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */; }; @@ -262,6 +265,7 @@ 484202C5D50983442D24D061 /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; }; 491D62ACD19E6F134B1766AF /* RoomNotificationSettingsUserDefinedScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3203C6566DC17B7AECC1B7FD /* RoomNotificationSettingsUserDefinedScreen.swift */; }; 492274DA6691EE985C2FCCAA /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 67E7A6F388D3BF85767609D9 /* Sentry */; }; + 4940B439681767BE9D78CFDB /* AppLockSetupBiometricsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F5EE5DE3B55D59299DB5BC /* AppLockSetupBiometricsScreenViewModelTests.swift */; }; 496CC9D59ACFAB84FD9B3B5F /* AnalyticsPromptScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840E86A67DB2C92C09771EAD /* AnalyticsPromptScreenModels.swift */; }; 49814A48470F347426513B07 /* TimelineReadReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1877038D1AD3D5A029F8AE2C /* TimelineReadReceiptsView.swift */; }; 49F2E7DD8CAACE09CEECE3E6 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */; }; @@ -460,6 +464,7 @@ 7F7EA51A9A43125A8CB6AC90 /* NotificationSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D560DDA3B20C82766ACFAD /* NotificationSettingsScreenViewModel.swift */; }; 7FB0BDE26838F1A92782D5E1 /* MediaUploadPreviewScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39B6C8690AEA1E49FF1BAF95 /* MediaUploadPreviewScreenUITests.swift */; }; 7FF27DA70D833CFC5724EFC5 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 1CAB56FF1DE17B3E871A0BA2 /* SnapshotTesting */; }; + 8015842CB4DE1BE414D2CDED /* AppLockSetupBiometricsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C62E07C1164F5120727A2A8 /* AppLockSetupBiometricsScreenCoordinator.swift */; }; 8024BE37156FF0A95A7A3465 /* AnalyticsPromptUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF11DD57D9FACF2A757AB024 /* AnalyticsPromptUITests.swift */; }; 804C15D8ADE0EA7A5268F58A /* OverridableAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648DD1C10E4957CB791FE0B8 /* OverridableAvatarImage.swift */; }; 80D00A7C62AAB44F54725C43 /* PermalinkBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */; }; @@ -587,6 +592,7 @@ 9EBDC79CAC9B63A0D626E333 /* LegalInformationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB2CAA266B921D128C35710 /* LegalInformationScreenCoordinator.swift */; }; 9F19096BFA629C0AC282B1E4 /* CreateRoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */; }; 9FAF6DA7E8E85C9699757764 /* CollapsibleRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E2656184491C505700D2405 /* CollapsibleRoomTimelineView.swift */; }; + 9FB41B0E8B2AA9B404E52C8B /* AppLockSetupBiometricsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CCC6C31102E1D8B9106DEDE /* AppLockSetupBiometricsScreenViewModelProtocol.swift */; }; A009BDFB0A6816D4C392ADCB /* SettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF715D4FD4710EBB637D661 /* SettingsScreenViewModelProtocol.swift */; }; A021827B528F1EDC9101CA58 /* AppCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */; }; A0A0D2A9564BDA3FDE2E360F /* FormattedBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */; }; @@ -705,6 +711,7 @@ BA4C9049BC96DED3A2F3B82E /* RoomNotificationSettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */; }; BB6BF528BC7F5B87E08C4F18 /* CameraPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8A3B7637DDBD6AA97AC2545 /* CameraPicker.swift */; }; BB784A02BADB03C820617A46 /* TextRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A55430639712CFACA34F43 /* TextRoomTimelineItem.swift */; }; + BB9B800C6094E34860E89DC5 /* AppLockSetupBiometricsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CCF9A924521DECA44778C4 /* AppLockSetupBiometricsScreen.swift */; }; BCC864190651B3A3CF51E4DF /* MediaFileHandleProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */; }; BD203FC6A7AE7637EA003643 /* RoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABDE6F66532CBEB0E016F94 /* RoomProxyMock.swift */; }; BD2BF1EC73FFB0C01552ECDA /* WelcomeScreenScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB782CE6176A5D2C082EC5D /* WelcomeScreenScreenModels.swift */; }; @@ -1035,7 +1042,9 @@ 0BC588051E6572A1AF51D738 /* TimelineSenderAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineSenderAvatarView.swift; sourceTree = ""; }; 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenViewModel.swift; sourceTree = ""; }; 0C34667458773B02AB5FB0B2 /* LegalInformationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenViewModel.swift; sourceTree = ""; }; + 0C62E07C1164F5120727A2A8 /* AppLockSetupBiometricsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreenCoordinator.swift; sourceTree = ""; }; 0C671107BDFC6CD1778C0B4C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + 0CCC6C31102E1D8B9106DEDE /* AppLockSetupBiometricsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreenViewModelProtocol.swift; sourceTree = ""; }; 0D0B159AFFBBD8ECFD0E37FA /* LoginScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenModels.swift; sourceTree = ""; }; 0D8F620C8B314840D8602E3F /* NSE.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = NSE.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 0DF5CBAF69BDF5DF31C661E1 /* IntentionalMentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentionalMentions.swift; sourceTree = ""; }; @@ -1050,6 +1059,7 @@ 105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFactory.swift; sourceTree = ""; }; 10B7F8EE25775DE2A305CBB5 /* NotificationCenterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterProtocol.swift; sourceTree = ""; }; 10CC626F97AD70FF0420C115 /* RoomSummaryProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProviderProtocol.swift; sourceTree = ""; }; + 10F130DF775CE6BC51A4E392 /* AppLockSetupBiometricsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreenModels.swift; sourceTree = ""; }; 10F32E0B4B83D2A11EE8D011 /* InviteUsersScreenSelectedItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenSelectedItem.swift; sourceTree = ""; }; 11151E78D6BB2B04A8FBD389 /* EmojiPickerScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenViewModelProtocol.swift; sourceTree = ""; }; 111B698739E3410E2CDB7144 /* MXLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXLog.swift; sourceTree = ""; }; @@ -1283,6 +1293,7 @@ 5281C5CDC4A712265A0B5FBF /* PollRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollRoomTimelineItem.swift; sourceTree = ""; }; 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedString.swift; sourceTree = ""; }; 52D7074991B3267B26D89B22 /* MockRoomTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineController.swift; sourceTree = ""; }; + 52F5EE5DE3B55D59299DB5BC /* AppLockSetupBiometricsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreenViewModelTests.swift; sourceTree = ""; }; 53482ECA4B6633961EC224F5 /* ScrollViewAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewAdapter.swift; sourceTree = ""; }; 5351EBD7A0B9610548E4B7B2 /* EncryptedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptedRoomTimelineItem.swift; sourceTree = ""; }; 536E72DCBEEC4A1FE66CFDCE /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; @@ -1679,6 +1690,7 @@ C936FDD017808FE416742D64 /* PollRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollRoomTimelineView.swift; sourceTree = ""; }; C97F8963B14EB0AF3940DDBF /* NotificationSettingsEditScreenRoomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenRoomCell.swift; sourceTree = ""; }; C99FDEEB71173C4C6FA2734C /* UserSessionFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinator.swift; sourceTree = ""; }; + C9F893F4A111CB7BA5C96949 /* AppLockSetupBiometricsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreenViewModel.swift; sourceTree = ""; }; CA28F29C9F93E93CC3C2C715 /* NavigationRootCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRootCoordinator.swift; sourceTree = ""; }; CA29952595B804DA221A0C1D /* ComposerToolbarViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerToolbarViewModelTests.swift; sourceTree = ""; }; CA2A71915C1F075E403F559C /* InvitesScreenCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenCell.swift; sourceTree = ""; }; @@ -1722,6 +1734,7 @@ D38391154120264910D19528 /* PollMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollMock.swift; sourceTree = ""; }; D39D7F513A36C9C1951DB44C /* AnalyticsSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreen.swift; sourceTree = ""; }; D3D455BC2423D911A62ACFB2 /* NSELogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSELogger.swift; sourceTree = ""; }; + D3F219838588C62198E726E3 /* LABiometryType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LABiometryType.swift; sourceTree = ""; }; D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreen.swift; sourceTree = ""; }; D49B9785E3AD7D1C15A29F2F /* MediaSourceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaSourceProxy.swift; sourceTree = ""; }; D4DA544B2520BFA65D6DB4BB /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; @@ -1837,6 +1850,7 @@ F7E8A8047B50E3607ACD354E /* ImageProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProviderProtocol.swift; sourceTree = ""; }; F875D71347DC81EAE7687446 /* NavigationRootCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRootCoordinatorTests.swift; sourceTree = ""; }; F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionScreenTests.swift; sourceTree = ""; }; + F8CCF9A924521DECA44778C4 /* AppLockSetupBiometricsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreen.swift; sourceTree = ""; }; F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomScreenUITests.swift; sourceTree = ""; }; F9E785D5137510481733A3E8 /* TextRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineView.swift; sourceTree = ""; }; F9ED8E731E21055F728E5FED /* TimelineStartRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStartRoomTimelineView.swift; sourceTree = ""; }; @@ -2162,7 +2176,9 @@ children = ( 3AD37D7DDF9904587601239D /* AppLockScreen */, CE39C9B97963CC30AB0859E5 /* AppLockSettingsScreen */, + 4570BFC8DD6665A91381F400 /* AppLockSetupBiometricsScreen */, 570026F1BA71A2D167652E48 /* AppLockSetupPINScreen */, + FB039572AA54E0690B4051AD /* Common */, ); path = AppLock; sourceTree = ""; @@ -2419,6 +2435,14 @@ path = MessageForwardingScreen; sourceTree = ""; }; + 33644C893C984570F605D94D /* View */ = { + isa = PBXGroup; + children = ( + F8CCF9A924521DECA44778C4 /* AppLockSetupBiometricsScreen.swift */, + ); + path = View; + sourceTree = ""; + }; 337015ADFBA3AB96660DB3A6 /* Generated */ = { isa = PBXGroup; children = ( @@ -2667,6 +2691,18 @@ path = Extensions; sourceTree = ""; }; + 4570BFC8DD6665A91381F400 /* AppLockSetupBiometricsScreen */ = { + isa = PBXGroup; + children = ( + 0C62E07C1164F5120727A2A8 /* AppLockSetupBiometricsScreenCoordinator.swift */, + 10F130DF775CE6BC51A4E392 /* AppLockSetupBiometricsScreenModels.swift */, + C9F893F4A111CB7BA5C96949 /* AppLockSetupBiometricsScreenViewModel.swift */, + 0CCC6C31102E1D8B9106DEDE /* AppLockSetupBiometricsScreenViewModelProtocol.swift */, + 33644C893C984570F605D94D /* View */, + ); + path = AppLockSetupBiometricsScreen; + sourceTree = ""; + }; 4775A7D6FBB210BF21318AD9 /* UserDetailsEditScreen */ = { isa = PBXGroup; children = ( @@ -3639,6 +3675,7 @@ AC0275CEE9CA078B34028BDF /* AppLockScreenViewModelTests.swift */, DD97F9661ABF08CE002054A2 /* AppLockServiceTests.swift */, B0BA67B3E4EF9D29D14A78CE /* AppLockSettingsScreenViewModelTests.swift */, + 52F5EE5DE3B55D59299DB5BC /* AppLockSetupBiometricsScreenViewModelTests.swift */, 989D7380D9C86B3A10D30B13 /* AppLockSetupPINScreenViewModelTests.swift */, 4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */, EF13BFD415CA84B1272E94F8 /* PINTextFieldTests.swift */, @@ -4470,6 +4507,14 @@ path = View; sourceTree = ""; }; + FB039572AA54E0690B4051AD /* Common */ = { + isa = PBXGroup; + children = ( + D3F219838588C62198E726E3 /* LABiometryType.swift */, + ); + path = Common; + sourceTree = ""; + }; FCDF06BDB123505F0334B4F9 /* Timeline */ = { isa = PBXGroup; children = ( @@ -4992,6 +5037,7 @@ 8478992479B296C45150208F /* AppLockScreenViewModelTests.swift in Sources */, 77693820498ABF3508814D49 /* AppLockServiceTests.swift in Sources */, 73F33E9776B7A50B65A031D2 /* AppLockSettingsScreenViewModelTests.swift in Sources */, + 4940B439681767BE9D78CFDB /* AppLockSetupBiometricsScreenViewModelTests.swift in Sources */, 25C4C1100B6EA79F5CC7CBB5 /* AppLockSetupPINScreenViewModelTests.swift in Sources */, 0EEC614342F823E5BF966C2C /* AppLockTimerTests.swift in Sources */, EA78A7512AFB1E5451744EB1 /* AppRouteURLParserTests.swift in Sources */, @@ -5149,6 +5195,11 @@ 0AD81E04A8C024C09B7AEAC5 /* AppLockSettingsScreenModels.swift in Sources */, A7BEE8216B4B12BE4C0F2C3F /* AppLockSettingsScreenViewModel.swift in Sources */, 0206016CCEF6EF9365916768 /* AppLockSettingsScreenViewModelProtocol.swift in Sources */, + BB9B800C6094E34860E89DC5 /* AppLockSetupBiometricsScreen.swift in Sources */, + 8015842CB4DE1BE414D2CDED /* AppLockSetupBiometricsScreenCoordinator.swift in Sources */, + 21813AF91CFC6F3E3896DB53 /* AppLockSetupBiometricsScreenModels.swift in Sources */, + 37906355E207DB5703754675 /* AppLockSetupBiometricsScreenViewModel.swift in Sources */, + 9FB41B0E8B2AA9B404E52C8B /* AppLockSetupBiometricsScreenViewModelProtocol.swift in Sources */, 733E2B19AB1FDA3B93293A28 /* AppLockSetupPINScreen.swift in Sources */, 9696ECAFB4F0C079C5C2A526 /* AppLockSetupPINScreenCoordinator.swift in Sources */, E4B07FF075C99D04D9AF792D /* AppLockSetupPINScreenModels.swift in Sources */, @@ -5331,6 +5382,7 @@ 1FE593ECEC40A43789105D80 /* KeychainController.swift in Sources */, FD29471C72872F8B7580E3E1 /* KeychainControllerMock.swift in Sources */, CB99B0FA38A4AC596F38CC13 /* KeychainControllerProtocol.swift in Sources */, + 454F8DDC4442C0DE54094902 /* LABiometryType.swift in Sources */, D5681C80D8281560AACE0035 /* Label.swift in Sources */, EEAE954289DE813A61656AE0 /* LayoutDirection.swift in Sources */, 42B084FDE621FBEE433AF444 /* LegalInformationScreen.swift in Sources */, diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index adc53b2bbe..f32360dbe0 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -100,6 +100,7 @@ "common_enter_your_pin" = "Enter your PIN"; "common_error" = "Error"; "common_everyone" = "Everyone"; +"common_face_id_ios" = "Face ID"; "common_file" = "File"; "common_forward_message" = "Forward message"; "common_gif" = "GIF"; @@ -116,6 +117,7 @@ "common_mute" = "Mute"; "common_no_results" = "No results"; "common_offline" = "Offline"; +"common_optic_id_ios" = "Optic ID"; "common_password" = "Password"; "common_people" = "People"; "common_permalink" = "Permalink"; @@ -152,6 +154,7 @@ "common_thread" = "Thread"; "common_topic" = "Topic"; "common_topic_placeholder" = "What is this room about?"; +"common_touch_id_ios" = "Touch ID"; "common_unable_to_decrypt" = "Unable to decrypt"; "common_unable_to_invite_message" = "Invites couldn't be sent to one or more users."; "common_unable_to_invite_title" = "Unable to send invite(s)"; @@ -265,6 +268,8 @@ "screen_analytics_prompt_third_party_sharing" = "We won't share your data with third parties"; "screen_analytics_prompt_title" = "Help improve %1$@"; "screen_analytics_settings_share_data" = "Share analytics data"; +"screen_app_lock_biometric_authentication" = "biometric authentication"; +"screen_app_lock_biometric_unlock" = "biometric unlock"; "screen_app_lock_forgot_pin" = "Forgot PIN?"; "screen_app_lock_settings_change_pin" = "Change PIN code"; "screen_app_lock_settings_enable_biometric_unlock" = "Allow biometric unlock"; @@ -274,6 +279,9 @@ "screen_app_lock_settings_remove_pin" = "Remove PIN"; "screen_app_lock_settings_remove_pin_alert_message" = "Are you sure you want to remove PIN?"; "screen_app_lock_settings_remove_pin_alert_title" = "Remove PIN?"; +"screen_app_lock_setup_biometric_unlock_allow_title" = "Allow %1$@"; +"screen_app_lock_setup_biometric_unlock_skip" = "I’d rather use PIN"; +"screen_app_lock_setup_biometric_unlock_subtitle" = "Save yourself some time and use %1$@ to unlock the app each time"; "screen_app_lock_setup_choose_pin" = "Choose PIN"; "screen_app_lock_setup_confirm_pin" = "Confirm PIN"; "screen_app_lock_setup_pin_blacklisted_dialog_content" = "You cannot choose this as your PIN code for security reasons"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 631b76b834..9094fd3122 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -220,6 +220,8 @@ public enum L10n { public static var commonError: String { return L10n.tr("Localizable", "common_error") } /// Everyone public static var commonEveryone: String { return L10n.tr("Localizable", "common_everyone") } + /// Face ID + public static var commonFaceIdIos: String { return L10n.tr("Localizable", "common_face_id_ios") } /// File public static var commonFile: String { return L10n.tr("Localizable", "common_file") } /// Forward message @@ -258,6 +260,8 @@ public enum L10n { public static var commonNoResults: String { return L10n.tr("Localizable", "common_no_results") } /// Offline public static var commonOffline: String { return L10n.tr("Localizable", "common_offline") } + /// Optic ID + public static var commonOpticIdIos: String { return L10n.tr("Localizable", "common_optic_id_ios") } /// Password public static var commonPassword: String { return L10n.tr("Localizable", "common_password") } /// People @@ -344,6 +348,8 @@ public enum L10n { public static var commonTopic: String { return L10n.tr("Localizable", "common_topic") } /// What is this room about? public static var commonTopicPlaceholder: String { return L10n.tr("Localizable", "common_topic_placeholder") } + /// Touch ID + public static var commonTouchIdIos: String { return L10n.tr("Localizable", "common_touch_id_ios") } /// Unable to decrypt public static var commonUnableToDecrypt: String { return L10n.tr("Localizable", "common_unable_to_decrypt") } /// Invites couldn't be sent to one or more users. @@ -648,6 +654,10 @@ public enum L10n { public static var screenAnalyticsSettingsReadTermsContentLink: String { return L10n.tr("Localizable", "screen_analytics_settings_read_terms_content_link") } /// Share analytics data public static var screenAnalyticsSettingsShareData: String { return L10n.tr("Localizable", "screen_analytics_settings_share_data") } + /// biometric authentication + public static var screenAppLockBiometricAuthentication: String { return L10n.tr("Localizable", "screen_app_lock_biometric_authentication") } + /// biometric unlock + public static var screenAppLockBiometricUnlock: String { return L10n.tr("Localizable", "screen_app_lock_biometric_unlock") } /// Forgot PIN? public static var screenAppLockForgotPin: String { return L10n.tr("Localizable", "screen_app_lock_forgot_pin") } /// Change PIN code @@ -666,6 +676,16 @@ public enum L10n { public static var screenAppLockSettingsRemovePinAlertMessage: String { return L10n.tr("Localizable", "screen_app_lock_settings_remove_pin_alert_message") } /// Remove PIN? public static var screenAppLockSettingsRemovePinAlertTitle: String { return L10n.tr("Localizable", "screen_app_lock_settings_remove_pin_alert_title") } + /// Allow %1$@ + public static func screenAppLockSetupBiometricUnlockAllowTitle(_ p1: Any) -> String { + return L10n.tr("Localizable", "screen_app_lock_setup_biometric_unlock_allow_title", String(describing: p1)) + } + /// I’d rather use PIN + public static var screenAppLockSetupBiometricUnlockSkip: String { return L10n.tr("Localizable", "screen_app_lock_setup_biometric_unlock_skip") } + /// Save yourself some time and use %1$@ to unlock the app each time + public static func screenAppLockSetupBiometricUnlockSubtitle(_ p1: Any) -> String { + return L10n.tr("Localizable", "screen_app_lock_setup_biometric_unlock_subtitle", String(describing: p1)) + } /// Choose PIN public static var screenAppLockSetupChoosePin: String { return L10n.tr("Localizable", "screen_app_lock_setup_choose_pin") } /// Confirm PIN diff --git a/ElementX/Sources/Screens/AppLock/AppLockSettingsScreen/AppLockSettingsScreenModels.swift b/ElementX/Sources/Screens/AppLock/AppLockSettingsScreen/AppLockSettingsScreenModels.swift index 911269a537..5049304941 100644 --- a/ElementX/Sources/Screens/AppLock/AppLockSettingsScreen/AppLockSettingsScreenModels.swift +++ b/ElementX/Sources/Screens/AppLock/AppLockSettingsScreen/AppLockSettingsScreenModels.swift @@ -28,21 +28,7 @@ struct AppLockSettingsScreenViewState: BindableState { var bindings: AppLockSettingsScreenViewStateBindings var supportsBiometry: Bool { biometryType != .none } - var enableBiometryTitle: String { - switch biometryType { - case .none: - return L10n.commonError - case .touchID: - return L10n.screenAppLockSettingsEnableTouchIdIos - case .faceID: - return L10n.screenAppLockSettingsEnableFaceIdIos - // Requires Xcode 15: - // case .opticID: - // L10n.screenAppLockSettingsEnableOpticIdIos - @unknown default: - return L10n.screenAppLockSettingsEnableBiometricUnlock - } - } + var enableBiometryTitle: String { L10n.screenAppLockSetupBiometricUnlockAllowTitle(biometryType.localizedString) } } struct AppLockSettingsScreenViewStateBindings { diff --git a/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenCoordinator.swift b/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenCoordinator.swift new file mode 100644 index 0000000000..85bb42a094 --- /dev/null +++ b/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenCoordinator.swift @@ -0,0 +1,60 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Combine +import SwiftUI + +struct AppLockSetupBiometricsScreenCoordinatorParameters { + let appLockService: AppLockServiceProtocol +} + +enum AppLockSetupBiometricsScreenCoordinatorAction { + case `continue` +} + +final class AppLockSetupBiometricsScreenCoordinator: CoordinatorProtocol { + private let parameters: AppLockSetupBiometricsScreenCoordinatorParameters + private var viewModel: AppLockSetupBiometricsScreenViewModelProtocol + private let actionsSubject: PassthroughSubject = .init() + private var cancellables = Set() + + var actions: AnyPublisher { + actionsSubject.eraseToAnyPublisher() + } + + init(parameters: AppLockSetupBiometricsScreenCoordinatorParameters) { + self.parameters = parameters + + viewModel = AppLockSetupBiometricsScreenViewModel(appLockService: parameters.appLockService) + } + + func start() { + viewModel.actions.sink { [weak self] action in + MXLog.info("Coordinator: received view model action: \(action)") + + guard let self else { return } + switch action { + case .continue: + self.actionsSubject.send(.continue) + } + } + .store(in: &cancellables) + } + + func toPresentable() -> AnyView { + AnyView(AppLockSetupBiometricsScreen(context: viewModel.context)) + } +} diff --git a/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenModels.swift b/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenModels.swift new file mode 100644 index 0000000000..6f250084fb --- /dev/null +++ b/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenModels.swift @@ -0,0 +1,38 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import LocalAuthentication +import SFSafeSymbols + +enum AppLockSetupBiometricsScreenViewModelAction { + case `continue` +} + +struct AppLockSetupBiometricsScreenViewState: BindableState { + /// The supported biometry type on this device. + let biometryType: LABiometryType + + var icon: SFSymbol { biometryType.systemSymbol } + var title: String { L10n.screenAppLockSetupBiometricUnlockAllowTitle(biometryType.localizedString) } + var subtitle: String { L10n.screenAppLockSetupBiometricUnlockSubtitle(biometryType.localizedString) } +} + +enum AppLockSetupBiometricsScreenViewAction { + /// The user would like to use Touch/Face ID. + case allow + /// The user doesn't want to use biometrics. + case skip +} diff --git a/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenViewModel.swift b/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenViewModel.swift new file mode 100644 index 0000000000..9948ac0f17 --- /dev/null +++ b/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenViewModel.swift @@ -0,0 +1,51 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Combine +import SwiftUI + +typealias AppLockSetupBiometricsScreenViewModelType = StateStoreViewModel + +class AppLockSetupBiometricsScreenViewModel: AppLockSetupBiometricsScreenViewModelType, AppLockSetupBiometricsScreenViewModelProtocol { + private let appLockService: AppLockServiceProtocol + private var actionsSubject: PassthroughSubject = .init() + + var actions: AnyPublisher { + actionsSubject.eraseToAnyPublisher() + } + + init(appLockService: AppLockServiceProtocol) { + self.appLockService = appLockService + super.init(initialViewState: AppLockSetupBiometricsScreenViewState(biometryType: appLockService.biometryType)) + } + + // MARK: - Public + + override func process(viewAction: AppLockSetupBiometricsScreenViewAction) { + MXLog.info("View model: received view action: \(viewAction)") + + switch viewAction { + case .allow: + MXLog.info("Enable biometric unlock.") + appLockService.biometricUnlockEnabled = true + actionsSubject.send(.continue) + case .skip: + MXLog.info("Disable biometric unlock.") + appLockService.biometricUnlockEnabled = false + actionsSubject.send(.continue) + } + } +} diff --git a/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenViewModelProtocol.swift b/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenViewModelProtocol.swift new file mode 100644 index 0000000000..0e38e53afb --- /dev/null +++ b/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/AppLockSetupBiometricsScreenViewModelProtocol.swift @@ -0,0 +1,23 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Combine + +@MainActor +protocol AppLockSetupBiometricsScreenViewModelProtocol { + var actions: AnyPublisher { get } + var context: AppLockSetupBiometricsScreenViewModelType.Context { get } +} diff --git a/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/View/AppLockSetupBiometricsScreen.swift b/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/View/AppLockSetupBiometricsScreen.swift new file mode 100644 index 0000000000..e311d0f710 --- /dev/null +++ b/ElementX/Sources/Screens/AppLock/AppLockSetupBiometricsScreen/View/AppLockSetupBiometricsScreen.swift @@ -0,0 +1,94 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Compound +import SwiftUI + +/// The screen shown when setting up App Lock that asks the user whether +/// the would like to use Face ID/Touch ID instead of entering their PIN code. +struct AppLockSetupBiometricsScreen: View { + @ObservedObject var context: AppLockSetupBiometricsScreenViewModel.Context + + var body: some View { + ScrollView { + VStack(spacing: 48) { + header + } + .padding(.horizontal, 16) + .padding(.top, UIConstants.iconTopPaddingToNavigationBar) + .frame(maxWidth: .infinity) + } + .toolbar(.visible, for: .navigationBar) + .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) + .safeAreaInset(edge: .bottom) { + buttons + .padding(.top, 16) + .padding(.horizontal, 16) + .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) + } + } + + var header: some View { + VStack(spacing: 8) { + Image(systemSymbol: context.viewState.icon) + .font(.system(size: 72)) + .padding(.top, 58) + .padding(.bottom, 26) + + Text(context.viewState.title) + .font(.compound.headingMDBold) + .multilineTextAlignment(.center) + .foregroundColor(.compound.textPrimary) + + Text(context.viewState.subtitle) + .font(.compound.bodyMD) + .multilineTextAlignment(.center) + .foregroundColor(.compound.textSecondary) + } + } + + var buttons: some View { + VStack(spacing: 16) { + Button(context.viewState.title) { context.send(viewAction: .allow) } + .buttonStyle(.compound(.primary)) + + Button { context.send(viewAction: .skip) } label: { + Text(L10n.screenAppLockSetupBiometricUnlockSkip) + .font(.compound.bodyLGSemibold) + .padding(14) + } + } + } +} + +// MARK: - Previews + +struct AppLockSetupBiometricsScreen_Previews: PreviewProvider, TestablePreview { + static let faceIDViewModel = AppLockSetupBiometricsScreenViewModel(appLockService: AppLockServiceMock.mock(biometryType: .faceID)) + static let touchIDViewModel = AppLockSetupBiometricsScreenViewModel(appLockService: AppLockServiceMock.mock(biometryType: .touchID)) + + static var previews: some View { + NavigationStack { + AppLockSetupBiometricsScreen(context: faceIDViewModel.context) + } + .previewDisplayName("Face ID") + + NavigationStack { + AppLockSetupBiometricsScreen(context: touchIDViewModel.context) + } + .previewDisplayName("Touch ID") + } +} diff --git a/ElementX/Sources/Screens/AppLock/Common/LABiometryType.swift b/ElementX/Sources/Screens/AppLock/Common/LABiometryType.swift new file mode 100644 index 0000000000..ada09d03ef --- /dev/null +++ b/ElementX/Sources/Screens/AppLock/Common/LABiometryType.swift @@ -0,0 +1,56 @@ +// +// Copyright 2023 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import LocalAuthentication +import SFSafeSymbols + +extension LABiometryType { + /// The SF Symbol that represents the biometry type. + var systemSymbol: SFSymbol { + switch self { + case .none: + MXLog.error("Invalid presentation: Biometry not supported.") + return .viewfinder + case .touchID: + return .touchid + case .faceID: + return .faceid + // Requires Xcode 15: + // case .opticID: + // .opticid + @unknown default: + return .viewfinder + } + } + + /// The localized string for the biometry type. + var localizedString: String { + switch self { + case .none: + MXLog.error("Invalid presentation: Biometry not supported.") + return L10n.screenAppLockBiometricUnlock + case .touchID: + return L10n.commonTouchIdIos + case .faceID: + return L10n.commonFaceIdIos + // Requires Xcode 15: + // case .opticID: + // L10n.commonOpticIdIos + @unknown default: + return L10n.screenAppLockBiometricUnlock + } + } +} diff --git a/UnitTests/Sources/AppLock/AppLockSetupBiometricsScreenViewModelTests.swift b/UnitTests/Sources/AppLock/AppLockSetupBiometricsScreenViewModelTests.swift new file mode 100644 index 0000000000..3393e85004 --- /dev/null +++ b/UnitTests/Sources/AppLock/AppLockSetupBiometricsScreenViewModelTests.swift @@ -0,0 +1,61 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +@testable import ElementX + +@MainActor +class AppLockSetupBiometricsScreenViewModelTests: XCTestCase { + var appLockService: AppLockService! + var keychainController: KeychainControllerMock! + var viewModel: AppLockSetupBiometricsScreenViewModelProtocol! + + var context: AppLockSetupBiometricsScreenViewModelType.Context { viewModel.context } + + override func setUp() { + AppSettings.reset() + keychainController = KeychainControllerMock() + appLockService = AppLockService(keychainController: keychainController, appSettings: AppSettings()) + viewModel = AppLockSetupBiometricsScreenViewModel(appLockService: appLockService) + } + + override func tearDown() { + AppSettings.reset() + } + + func testAllow() async throws { + // Given a service that has biometric unlock disabled. + XCTAssertFalse(appLockService.biometricUnlockEnabled) + + // When allowing Touch/Face ID. + context.send(viewAction: .allow) + + // Then the service should now have biometric unlock enabled. + XCTAssertTrue(appLockService.biometricUnlockEnabled) + } + + func testSkip() async throws { + // Given a service that has biometric unlock disabled. + XCTAssertFalse(appLockService.biometricUnlockEnabled) + + // When skipping biometrics. + context.send(viewAction: .skip) + + // Then the service should now have biometric unlock enabled. + XCTAssertFalse(appLockService.biometricUnlockEnabled) + } +} diff --git a/UnitTests/__Snapshots__/PreviewTests/test_appLockSetupBiometricsScreen.Face-ID.png b/UnitTests/__Snapshots__/PreviewTests/test_appLockSetupBiometricsScreen.Face-ID.png new file mode 100644 index 0000000000..66f75a40b1 --- /dev/null +++ b/UnitTests/__Snapshots__/PreviewTests/test_appLockSetupBiometricsScreen.Face-ID.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb397dc5c399e5859733ae04c2057a25bc53e1fbe97d91a2407c01aa562d18e2 +size 99917 diff --git a/UnitTests/__Snapshots__/PreviewTests/test_appLockSetupBiometricsScreen.Touch-ID.png b/UnitTests/__Snapshots__/PreviewTests/test_appLockSetupBiometricsScreen.Touch-ID.png new file mode 100644 index 0000000000..26ca1de3ff --- /dev/null +++ b/UnitTests/__Snapshots__/PreviewTests/test_appLockSetupBiometricsScreen.Touch-ID.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b970f859c66bb2037ca1a509c9874b624967afbd0b97b05ef0b6172a921c370f +size 111799 diff --git a/changelog.d/pr-1942.wip b/changelog.d/pr-1942.wip new file mode 100644 index 0000000000..0de0e7d904 --- /dev/null +++ b/changelog.d/pr-1942.wip @@ -0,0 +1 @@ +Add Biometrics screen for enabling Touch/Face ID after creating a PIN. \ No newline at end of file