Skip to content

Commit

Permalink
Add AppLockSettingsScreen.
Browse files Browse the repository at this point in the history
# Conflicts:
#	ElementX.xcodeproj/project.pbxproj
#	ElementX/Sources/Services/AppLock/AppLockService.swift
  • Loading branch information
pixlwave committed Oct 19, 2023
1 parent 99c2878 commit 10f728c
Show file tree
Hide file tree
Showing 36 changed files with 776 additions and 78 deletions.
128 changes: 92 additions & 36 deletions ElementX.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@
"untranslated" = "Untranslated";

"screen_app_lock_title" = "%@ is locked";
"screen_app_lock_settings_change_pin" = "Change PIN code";
"screen_app_lock_settings_remove_pin" = "Remove PIN";
"screen_app_lock_settings_enable_touch_id_ios" = "Allow Touch ID";
"screen_app_lock_settings_enable_face_id_ios" = "Allow Face ID";
"screen_app_lock_settings_enable_optic_id_ios" = "Allow Optic ID";
"screen_app_lock_settings_enable_biometric_unlock" = "Allow biometric unlock";
"screen_app_lock_settings_remove_pin_alert_title" = "Remove PIN?";
"screen_app_lock_settings_remove_pin_alert_message" = "Are you sure you want to remove PIN?";
"common_unlock" = "Unlock";
"common_screen_lock" = "Screen lock";

// MARK: - Soft logout

Expand Down
1 change: 1 addition & 0 deletions ElementX/Sources/Application/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
let navigationSplitCoordinator = NavigationSplitCoordinator(placeholderCoordinator: PlaceholderScreenCoordinator())
let userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession,
navigationSplitCoordinator: navigationSplitCoordinator,
appLockService: appLockFlowCoordinator.appLockService,
bugReportService: ServiceLocator.shared.bugReportService,
roomTimelineControllerFactory: RoomTimelineControllerFactory(),
appSettings: appSettings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum UserSessionFlowCoordinatorAction {
class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
private let userSession: UserSessionProtocol
private let navigationSplitCoordinator: NavigationSplitCoordinator
private let appLockService: AppLockServiceProtocol
private let bugReportService: BugReportServiceProtocol
private let roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol
private let appSettings: AppSettings
Expand All @@ -48,13 +49,15 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {

init(userSession: UserSessionProtocol,
navigationSplitCoordinator: NavigationSplitCoordinator,
appLockService: AppLockServiceProtocol,
bugReportService: BugReportServiceProtocol,
roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol,
appSettings: AppSettings,
analytics: AnalyticsService) {
stateMachine = UserSessionFlowCoordinatorStateMachine()
self.userSession = userSession
self.navigationSplitCoordinator = navigationSplitCoordinator
self.appLockService = appLockService
self.bugReportService = bugReportService
self.roomTimelineControllerFactory = roomTimelineControllerFactory
self.appSettings = appSettings
Expand Down Expand Up @@ -328,6 +331,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
let parameters = SettingsScreenCoordinatorParameters(navigationStackCoordinator: settingsNavigationStackCoordinator,
userIndicatorController: userIndicatorController,
userSession: userSession,
appLockService: appLockService,
bugReportService: bugReportService,
notificationSettings: userSession.clientProxy.notificationSettings,
appSettings: appSettings)
Expand Down
18 changes: 18 additions & 0 deletions ElementX/Sources/Generated/Strings+Untranslated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,26 @@ import Foundation
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
public enum UntranslatedL10n {
/// Screen lock
public static var commonScreenLock: String { return UntranslatedL10n.tr("Untranslated", "common_screen_lock") }
/// Unlock
public static var commonUnlock: String { return UntranslatedL10n.tr("Untranslated", "common_unlock") }
/// Change PIN code
public static var screenAppLockSettingsChangePin: String { return UntranslatedL10n.tr("Untranslated", "screen_app_lock_settings_change_pin") }
/// Allow biometric unlock
public static var screenAppLockSettingsEnableBiometricUnlock: String { return UntranslatedL10n.tr("Untranslated", "screen_app_lock_settings_enable_biometric_unlock") }
/// Allow Face ID
public static var screenAppLockSettingsEnableFaceIdIos: String { return UntranslatedL10n.tr("Untranslated", "screen_app_lock_settings_enable_face_id_ios") }
/// Allow Optic ID
public static var screenAppLockSettingsEnableOpticIdIos: String { return UntranslatedL10n.tr("Untranslated", "screen_app_lock_settings_enable_optic_id_ios") }
/// Allow Touch ID
public static var screenAppLockSettingsEnableTouchIdIos: String { return UntranslatedL10n.tr("Untranslated", "screen_app_lock_settings_enable_touch_id_ios") }
/// Remove PIN
public static var screenAppLockSettingsRemovePin: String { return UntranslatedL10n.tr("Untranslated", "screen_app_lock_settings_remove_pin") }
/// Are you sure you want to remove PIN?
public static var screenAppLockSettingsRemovePinAlertMessage: String { return UntranslatedL10n.tr("Untranslated", "screen_app_lock_settings_remove_pin_alert_message") }
/// Remove PIN?
public static var screenAppLockSettingsRemovePinAlertTitle: String { return UntranslatedL10n.tr("Untranslated", "screen_app_lock_settings_remove_pin_alert_title") }
/// %@ is locked
public static func screenAppLockTitle(_ p1: Any) -> String {
return UntranslatedL10n.tr("Untranslated", "screen_app_lock_title", String(describing: p1))
Expand Down
127 changes: 125 additions & 2 deletions ElementX/Sources/Mocks/Generated/GeneratedMocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
// DO NOT EDIT

// swiftlint:disable all
import AnalyticsEvents
import Combine
import Foundation
import SwiftUI
import AnalyticsEvents
import LocalAuthentication
import MatrixRustSDK
import SwiftUI
class AnalyticsClientMock: AnalyticsClientProtocol {
var isRunning: Bool {
get { return underlyingIsRunning }
Expand Down Expand Up @@ -115,6 +116,128 @@ class AnalyticsClientMock: AnalyticsClientProtocol {
updateUserPropertiesClosure?(userProperties)
}
}
class AppLockServiceMock: AppLockServiceProtocol {
var isEnabled: Bool {
get { return underlyingIsEnabled }
set(value) { underlyingIsEnabled = value }
}
var underlyingIsEnabled: Bool!
var biometryType: LABiometryType {
get { return underlyingBiometryType }
set(value) { underlyingBiometryType = value }
}
var underlyingBiometryType: LABiometryType!
var biometricUnlockEnabled: Bool {
get { return underlyingBiometricUnlockEnabled }
set(value) { underlyingBiometricUnlockEnabled = value }
}
var underlyingBiometricUnlockEnabled: Bool!

//MARK: - setupPINCode

var setupPINCodeCallsCount = 0
var setupPINCodeCalled: Bool {
return setupPINCodeCallsCount > 0
}
var setupPINCodeReceivedPinCode: String?
var setupPINCodeReceivedInvocations: [String] = []
var setupPINCodeReturnValue: Result<Void, AppLockServiceError>!
var setupPINCodeClosure: ((String) -> Result<Void, AppLockServiceError>)?

func setupPINCode(_ pinCode: String) -> Result<Void, AppLockServiceError> {
setupPINCodeCallsCount += 1
setupPINCodeReceivedPinCode = pinCode
setupPINCodeReceivedInvocations.append(pinCode)
if let setupPINCodeClosure = setupPINCodeClosure {
return setupPINCodeClosure(pinCode)
} else {
return setupPINCodeReturnValue
}
}
//MARK: - disable

var disableCallsCount = 0
var disableCalled: Bool {
return disableCallsCount > 0
}
var disableClosure: (() -> Void)?

func disable() {
disableCallsCount += 1
disableClosure?()
}
//MARK: - applicationDidEnterBackground

var applicationDidEnterBackgroundCallsCount = 0
var applicationDidEnterBackgroundCalled: Bool {
return applicationDidEnterBackgroundCallsCount > 0
}
var applicationDidEnterBackgroundClosure: (() -> Void)?

func applicationDidEnterBackground() {
applicationDidEnterBackgroundCallsCount += 1
applicationDidEnterBackgroundClosure?()
}
//MARK: - computeNeedsUnlock

var computeNeedsUnlockWillEnterForegroundAtCallsCount = 0
var computeNeedsUnlockWillEnterForegroundAtCalled: Bool {
return computeNeedsUnlockWillEnterForegroundAtCallsCount > 0
}
var computeNeedsUnlockWillEnterForegroundAtReceivedDate: Date?
var computeNeedsUnlockWillEnterForegroundAtReceivedInvocations: [Date] = []
var computeNeedsUnlockWillEnterForegroundAtReturnValue: Bool!
var computeNeedsUnlockWillEnterForegroundAtClosure: ((Date) -> Bool)?

func computeNeedsUnlock(willEnterForegroundAt date: Date) -> Bool {
computeNeedsUnlockWillEnterForegroundAtCallsCount += 1
computeNeedsUnlockWillEnterForegroundAtReceivedDate = date
computeNeedsUnlockWillEnterForegroundAtReceivedInvocations.append(date)
if let computeNeedsUnlockWillEnterForegroundAtClosure = computeNeedsUnlockWillEnterForegroundAtClosure {
return computeNeedsUnlockWillEnterForegroundAtClosure(date)
} else {
return computeNeedsUnlockWillEnterForegroundAtReturnValue
}
}
//MARK: - unlock

var unlockWithCallsCount = 0
var unlockWithCalled: Bool {
return unlockWithCallsCount > 0
}
var unlockWithReceivedPinCode: String?
var unlockWithReceivedInvocations: [String] = []
var unlockWithReturnValue: Bool!
var unlockWithClosure: ((String) -> Bool)?

func unlock(with pinCode: String) -> Bool {
unlockWithCallsCount += 1
unlockWithReceivedPinCode = pinCode
unlockWithReceivedInvocations.append(pinCode)
if let unlockWithClosure = unlockWithClosure {
return unlockWithClosure(pinCode)
} else {
return unlockWithReturnValue
}
}
//MARK: - unlockWithBiometrics

var unlockWithBiometricsCallsCount = 0
var unlockWithBiometricsCalled: Bool {
return unlockWithBiometricsCallsCount > 0
}
var unlockWithBiometricsReturnValue: Bool!
var unlockWithBiometricsClosure: (() -> Bool)?

func unlockWithBiometrics() -> Bool {
unlockWithBiometricsCallsCount += 1
if let unlockWithBiometricsClosure = unlockWithBiometricsClosure {
return unlockWithBiometricsClosure()
} else {
return unlockWithBiometricsReturnValue
}
}
}
class AudioConverterMock: AudioConverterProtocol {

//MARK: - convertToOpusOgg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ struct AppLockScreen: View {

// Add TestablePreview conformance once we have designs.
struct AppLockScreen_Previews: PreviewProvider {
static let viewModel = AppLockScreenViewModel(appLockService: AppLockService(keychainController: KeychainControllerMock(),
appSettings: ServiceLocator.shared.settings))
static let viewModel = AppLockScreenViewModel(appLockService: AppLockServiceMock.mock())

static var previews: some View {
NavigationStack {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// 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 AppLockSettingsScreenCoordinatorParameters {
let appLockService: AppLockServiceProtocol
}

enum AppLockSettingsScreenCoordinatorAction {
case done
}

final class AppLockSettingsScreenCoordinator: CoordinatorProtocol {
private let parameters: AppLockSettingsScreenCoordinatorParameters
private var viewModel: AppLockSettingsScreenViewModelProtocol
private let actionsSubject: PassthroughSubject<AppLockSettingsScreenCoordinatorAction, Never> = .init()
private var cancellables = Set<AnyCancellable>()

var actions: AnyPublisher<AppLockSettingsScreenCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher()
}

init(parameters: AppLockSettingsScreenCoordinatorParameters) {
self.parameters = parameters

viewModel = AppLockSettingsScreenViewModel(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 .changePINCode:
break
case .done:
self.actionsSubject.send(.done)
}
}
.store(in: &cancellables)
}

func toPresentable() -> AnyView {
AnyView(AppLockSettingsScreen(context: viewModel.context))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// 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 Foundation
import LocalAuthentication

enum AppLockSettingsScreenViewModelAction {
/// The user would like to enter new PIN code.
case changePINCode
case done
}

struct AppLockSettingsScreenViewState: BindableState {
let biometryType: LABiometryType
var bindings: AppLockSettingsScreenViewStateBindings

var supportsBiometry: Bool { biometryType != .none }
var enableBiometryTitle: String {
switch biometryType {
case .none:
L10n.commonError
case .touchID:
UntranslatedL10n.screenAppLockSettingsEnableTouchIdIos
case .faceID:
UntranslatedL10n.screenAppLockSettingsEnableFaceIdIos
case .opticID:
UntranslatedL10n.screenAppLockSettingsEnableOpticIdIos
@unknown default:
UntranslatedL10n.screenAppLockSettingsEnableBiometricUnlock
}
}
}

struct AppLockSettingsScreenViewStateBindings {
var enableBiometrics: Bool
var alertInfo: AlertInfo<AppLockSettingsScreenAlertType>?
}

enum AppLockSettingsScreenAlertType {
/// The alert shown to confirm the user would like to remove their PIN.
case confirmRemovePINCode
}

enum AppLockSettingsScreenViewAction {
/// The user would like to enter a new PIN code.
case changePINCode
/// The user would like to disable the App Lock feature.
case disable
/// The user has toggled the biometrics setting.
case enableBiometricsChanged
case done
}
Loading

0 comments on commit 10f728c

Please sign in to comment.