From aa045bbe737a856ce35501f87031d1d56e931dac Mon Sep 17 00:00:00 2001 From: akashmeruva9 Date: Thu, 12 Sep 2024 19:55:18 +0530 Subject: [PATCH 1/2] CMP Mifos Passcode Module Setup --- gradle/libs.versions.toml | 12 +- iosApp/iosApp.xcodeproj/project.pbxproj | 20 ++ iosApp/iosApp/Biometric/BiometricUtil.swift | 112 +++++++ iosApp/iosApp/Biometric/CipherUtil.swift | 102 ++++++ iosApp/iosApp/ContentView.swift | 27 +- iosApp/iosApp/Info.plist | 50 +++ libs/cmp-mifos-passcode/build.gradle.kts | 86 +++++ libs/cmp-mifos-passcode/shared.podspec | 54 +++ .../passcode/BiometricUtilAndroidImpl.kt | 132 ++++++++ .../mifos/passcode/CipherUtilImpl.android.kt | 61 ++++ .../com/mifos/passcode/Platform.android.kt | 10 + .../kotlin/com/mifos/passcode/getPlatform.kt | 2 + .../composeResources/drawable/ic_delete.xml | 5 + .../composeResources/drawable/mifos_logo.jpg | Bin 0 -> 75684 bytes .../composeResources/font/lato_black.ttf | Bin 0 -> 69500 bytes .../composeResources/font/lato_bold.ttf | Bin 0 -> 73332 bytes .../composeResources/font/lato_regular.ttf | Bin 0 -> 75152 bytes .../composeResources/values/strings.xml | 34 ++ .../com/mifos/passcode/CipherUtilImpl.kt | 18 + .../kotlin/com/mifos/passcode/Greeting.kt | 11 + .../com/mifos/passcode/PasscodeNavigation.kt | 47 +++ .../kotlin/com/mifos/passcode/Platform.kt | 7 + .../com/mifos/passcode/component/BackSpace.kt | 45 +++ .../com/mifos/passcode/component/MifosIcon.kt | 26 ++ .../passcode/component/PassCodeScreen.kt | 309 ++++++++++++++++++ .../PasscodeBiometricConfirmDialog.kt | 116 +++++++ .../passcode/component/PasscodeButton.kt | 91 ++++++ .../passcode/component/PasscodeHeader.kt | 114 +++++++ .../mifos/passcode/component/PasscodeKeys.kt | 199 +++++++++++ .../component/PasscodeMismatchedDialog.kt | 37 +++ .../component/PasscodeStepIndicator.kt | 50 +++ .../passcode/component/PasscodeToolbar.kt | 80 +++++ .../mifos/passcode/component/Visibility.kt | 58 ++++ .../mifos/passcode/component/VisibilityOff.kt | 38 +++ .../mifos/passcode/data/PasscodeRepository.kt | 8 + .../passcode/data/PasscodeRepositoryImpl.kt | 23 ++ .../data/SetBiometricPublicKeyRepository.kt | 17 + .../kotlin/com/mifos/passcode/theme/Color.kt | 5 + .../kotlin/com/mifos/passcode/theme/Font.kt | 30 ++ .../kotlin/com/mifos/passcode/theme/Theme.kt | 40 +++ .../kotlin/com/mifos/passcode/theme/Type.kt | 57 ++++ .../mifos/passcode/utility/BioMetricUtil.kt | 21 ++ .../com/mifos/passcode/utility/Constants.kt | 7 + .../passcode/utility/PreferenceManager.kt | 36 ++ .../mifos/passcode/utility/ShakeAnimation.kt | 28 ++ .../kotlin/com/mifos/passcode/utility/Step.kt | 6 + .../BiometricAuthorizationViewModel.kt | 85 +++++ .../passcode/viewmodels/PasscodeViewModel.kt | 146 +++++++++ .../mifos/passcode/CipherUtilImpl.desktop.kt | 5 + .../com/mifos/passcode/Platform.desktop.kt | 4 + .../kotlin/com/mifos/passcode/App.ios.kt | 26 ++ .../com/mifos/passcode/CipherUtilImpl.ios.kt | 12 + .../kotlin/com/mifos/passcode/Platform.ios.kt | 8 + mifospay/build.gradle.kts | 2 +- .../main/java/org/mifospay/MainActivity.kt | 24 +- .../org/mifospay/MainActivityViewModel.kt | 5 +- .../org/mifospay/navigation/LoginNavGraph.kt | 2 +- .../mifospay/navigation/PasscodeNavGraph.kt | 17 +- .../org/mifospay/navigation/RootNavGraph.kt | 11 +- mifospay/src/main/res/values/strings.xml | 4 +- settings.gradle.kts | 2 + shared/build.gradle.kts | 10 +- .../shared/preferences/DataStoreModule.ios.kt | 5 + 63 files changed, 2577 insertions(+), 22 deletions(-) create mode 100644 iosApp/iosApp/Biometric/BiometricUtil.swift create mode 100644 iosApp/iosApp/Biometric/CipherUtil.swift create mode 100644 iosApp/iosApp/Info.plist create mode 100644 libs/cmp-mifos-passcode/build.gradle.kts create mode 100644 libs/cmp-mifos-passcode/shared.podspec create mode 100644 libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/BiometricUtilAndroidImpl.kt create mode 100644 libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/CipherUtilImpl.android.kt create mode 100644 libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/Platform.android.kt create mode 100644 libs/cmp-mifos-passcode/src/appleMain/kotlin/com/mifos/passcode/getPlatform.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/composeResources/drawable/ic_delete.xml create mode 100644 libs/cmp-mifos-passcode/src/commonMain/composeResources/drawable/mifos_logo.jpg create mode 100644 libs/cmp-mifos-passcode/src/commonMain/composeResources/font/lato_black.ttf create mode 100644 libs/cmp-mifos-passcode/src/commonMain/composeResources/font/lato_bold.ttf create mode 100644 libs/cmp-mifos-passcode/src/commonMain/composeResources/font/lato_regular.ttf create mode 100644 libs/cmp-mifos-passcode/src/commonMain/composeResources/values/strings.xml create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/CipherUtilImpl.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/Greeting.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/PasscodeNavigation.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/Platform.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/BackSpace.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/MifosIcon.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PassCodeScreen.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeBiometricConfirmDialog.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeButton.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeHeader.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeKeys.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeMismatchedDialog.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeStepIndicator.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeToolbar.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/Visibility.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/VisibilityOff.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/PasscodeRepository.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/PasscodeRepositoryImpl.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/SetBiometricPublicKeyRepository.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Color.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Font.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Theme.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Type.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/BioMetricUtil.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/Constants.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/PreferenceManager.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/ShakeAnimation.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/Step.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/viewmodels/BiometricAuthorizationViewModel.kt create mode 100644 libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/viewmodels/PasscodeViewModel.kt create mode 100644 libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/CipherUtilImpl.desktop.kt create mode 100644 libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/Platform.desktop.kt create mode 100644 libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/App.ios.kt create mode 100644 libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/CipherUtilImpl.ios.kt create mode 100644 libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/Platform.ios.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 88d3d2a5e..b1f32f51e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,4 +1,5 @@ [versions] +agp = "8.3.0" accompanistPagerVersion = "0.34.0" activityVersion = "1.9.1" androidDesugarJdkLibs = "2.0.4" @@ -81,6 +82,10 @@ twitter-detekt-compose = "0.0.26" versionCatalogLinterVersion = "1.0.3" wire = "5.0.0" zxingVersion = "3.5.3" +lifecycleViewmodelKtx = "2.8.1" +navigationComposeVersion = "2.7.0-alpha07" +multiplatformSettings = "1.0.0" +biometricKtx = "1.1.0" [libraries] accompanist-pager = { group = "com.google.accompanist", name = "accompanist-pager", version.ref = "accompanistPagerVersion" } @@ -198,6 +203,11 @@ squareup-retrofit2 = { group = "com.squareup.retrofit2", name = "retrofit", vers truth = { group = "com.google.truth", name = "truth", version.ref = "truth" } twitter-detekt-compose = { group = "com.twitter.compose.rules", name = "detekt", version.ref = "twitter-detekt-compose" } zxing = { group = "com.google.zxing", name = "core", version.ref = "zxingVersion" } +navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigationComposeVersion" } +multiplatform-settings-no-arg = { module = "com.russhwolf:multiplatform-settings-no-arg", version.ref = "multiplatformSettings" } +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } +androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "biometricKtx" } +androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "lifecycleViewmodelKtx" } [bundles] androidx-compose-ui-test = [ @@ -247,4 +257,4 @@ room = { id = "androidx.room", version.ref = "room" } secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } spotless = { id = "com.diffplug.spotless", version.ref = "spotlessVersion" } version-catalog-linter = { id = "io.github.pemistahl.version-catalog-linter", version.ref = "versionCatalogLinterVersion" } -wire = { id = "com.squareup.wire", version.ref = "wire" } +wire = { id = "com.squareup.wire", version.ref = "wire" } \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj index f20cd9102..f4913f99e 100644 --- a/iosApp/iosApp.xcodeproj/project.pbxproj +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -11,6 +11,9 @@ 189117BF2C6CC76200DABAA8 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 189117BE2C6CC76200DABAA8 /* ContentView.swift */; }; 189117C12C6CC76400DABAA8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 189117C02C6CC76400DABAA8 /* Assets.xcassets */; }; 189117C42C6CC76400DABAA8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 189117C32C6CC76400DABAA8 /* Preview Assets.xcassets */; }; + 8E096B732C90B3B600C3BEA6 /* BiometricUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E096B722C90B3B600C3BEA6 /* BiometricUtil.swift */; }; + 8E096B752C90B3C100C3BEA6 /* CipherUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E096B742C90B3C100C3BEA6 /* CipherUtil.swift */; }; + 8EAA94DE2C92A681005081E9 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 8EAA94DD2C92A681005081E9 /* Info.plist */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -19,6 +22,9 @@ 189117BE2C6CC76200DABAA8 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 189117C02C6CC76400DABAA8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 189117C32C6CC76400DABAA8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 8E096B722C90B3B600C3BEA6 /* BiometricUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BiometricUtil.swift; sourceTree = ""; }; + 8E096B742C90B3C100C3BEA6 /* CipherUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CipherUtil.swift; sourceTree = ""; }; + 8EAA94DD2C92A681005081E9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -51,7 +57,9 @@ 189117BB2C6CC76200DABAA8 /* iosApp */ = { isa = PBXGroup; children = ( + 8E096B712C90B3A000C3BEA6 /* Biometric */, 189117BC2C6CC76200DABAA8 /* iosAppApp.swift */, + 8EAA94DD2C92A681005081E9 /* Info.plist */, 189117BE2C6CC76200DABAA8 /* ContentView.swift */, 189117C02C6CC76400DABAA8 /* Assets.xcassets */, 189117C22C6CC76400DABAA8 /* Preview Content */, @@ -67,6 +75,15 @@ path = "Preview Content"; sourceTree = ""; }; + 8E096B712C90B3A000C3BEA6 /* Biometric */ = { + isa = PBXGroup; + children = ( + 8E096B722C90B3B600C3BEA6 /* BiometricUtil.swift */, + 8E096B742C90B3C100C3BEA6 /* CipherUtil.swift */, + ); + path = Biometric; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -128,6 +145,7 @@ files = ( 189117C42C6CC76400DABAA8 /* Preview Assets.xcassets in Resources */, 189117C12C6CC76400DABAA8 /* Assets.xcassets in Resources */, + 8EAA94DE2C92A681005081E9 /* Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -158,6 +176,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8E096B752C90B3C100C3BEA6 /* CipherUtil.swift in Sources */, + 8E096B732C90B3B600C3BEA6 /* BiometricUtil.swift in Sources */, 189117BF2C6CC76200DABAA8 /* ContentView.swift in Sources */, 189117BD2C6CC76200DABAA8 /* iosAppApp.swift in Sources */, ); diff --git a/iosApp/iosApp/Biometric/BiometricUtil.swift b/iosApp/iosApp/Biometric/BiometricUtil.swift new file mode 100644 index 000000000..e9910c3d5 --- /dev/null +++ b/iosApp/iosApp/Biometric/BiometricUtil.swift @@ -0,0 +1,112 @@ +import Foundation +import shared +import LocalAuthentication + +class BiometricUtilIosImpl: BioMetricUtil { + + private let cipherUtil = CipherUtilIosImpl() + + private var promptDescription: String = "Authenticate" + + func setAndReturnPublicKey(completionHandler: @escaping (String?, (any Error)?) -> Void) { + + let laContext = LAContext() + laContext.localizedReason = promptDescription + laContext.localizedFallbackTitle = "Cancel" + laContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: promptDescription) { + [weak self] success, authenticationError in + + DispatchQueue.main.async { + if success { + completionHandler(self?.generatePublicKey(), nil) + } else { + completionHandler(nil, authenticationError) + } + } + } + } + + func authenticate() async throws -> AuthenticationResult { + do { + _ = try self.cipherUtil.getCrypto() + return AuthenticationResult.Success() + } catch { + print("AuthenticateError: \(error.localizedDescription)") + return AuthenticationResult.Error(error: error.localizedDescription) + } + } + + func canAuthenticate() -> Bool { + var error: NSError? + let laContext = LAContext() + return laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) + } + + + func generatePublicKey() -> String? { + let keyPair = try? cipherUtil.generateKeyPair() + return keyPair?.publicKey?.toPemFormat().toBase64() + } + + func getPublicKey() -> String? { + return cipherUtil.getPublicKey()?.encoded?.toPemFormat().toBase64() + } + + func isBiometricSet() -> Bool { + return (getPublicKey() != nil) && isValidCrypto() + } + + func isValidCrypto() -> Bool { + do { + _ = try cipherUtil.getCrypto() + return true + } catch { + return false + } + } + + func signUserId(ucc: String) -> String { + guard let data = ucc.data(using: .utf8) else { + print("Failed to convert UCC to data") + fatalError() + } + + var error: Unmanaged? + guard let signature = SecKeyCreateSignature(cipherUtil.getKey()!, .rsaSignatureMessagePKCS1v15SHA256, data as CFData, &error) else { + if let error = error { + print("Error creating signature: \(error.takeRetainedValue())") + } + fatalError() + } + + return (signature as Data).base64EncodedString() + } + + +} + +extension String { + func toPemFormat() -> String { + let chunkSize = 64 + var pemString = "-----BEGIN RSA PUBLIC KEY-----\n" + var base64String = self + while base64String.count > 0 { + let chunkIndex = base64String.index(base64String.startIndex, offsetBy: min(chunkSize, base64String.count)) + let chunk = base64String[.. String? { + guard let data = self.data(using: .utf8) else { + return nil + } + return data.base64EncodedString() + } +} \ No newline at end of file diff --git a/iosApp/iosApp/Biometric/CipherUtil.swift b/iosApp/iosApp/Biometric/CipherUtil.swift new file mode 100644 index 000000000..2b1daea27 --- /dev/null +++ b/iosApp/iosApp/Biometric/CipherUtil.swift @@ -0,0 +1,102 @@ +import Foundation +import shared + +class CipherUtilIosImpl: ICipherUtil { + private let KEY_NAME = "my_biometric_key" + private let tag: Data + + private lazy var key: SecKey? = { + let query: [String: Any] = [ + kSecClass as String: kSecClassKey, + kSecAttrApplicationTag as String: tag, + kSecAttrKeyType as String: kSecAttrKeyTypeRSA, + kSecReturnRef as String: true + ] + var item: CFTypeRef? + let status = SecItemCopyMatching(query as CFDictionary, &item) + guard status == errSecSuccess else { return nil } + return (item as! SecKey) + }() + + init() { + self.tag = KEY_NAME.data(using: .utf8)! + } + + func generateKeyPair() throws -> CommonKeyPair { + let access = SecAccessControlCreateWithFlags( + nil, + kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, + .userPresence, + nil + )! + let attributes: [String: Any] = [ + kSecAttrKeyType as String: kSecAttrKeyTypeRSA, + kSecAttrKeySizeInBits as String: 2048, + kSecPrivateKeyAttrs as String: [ + kSecAttrIsPermanent as String: true, + kSecAttrApplicationTag as String: tag, + kSecAttrAccessControl as String: access, + ] + ] + + var error: Unmanaged? + guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { + throw error!.takeRetainedValue() as Error + } + guard let publicKey = SecKeyCopyPublicKey(privateKey) else { + throw NSError(domain: NSOSStatusErrorDomain, code: Int(errSecInternalError), userInfo: nil) + } + + let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil)! as Data + let privateKeyData = SecKeyCopyExternalRepresentation(privateKey, nil)! as Data + + return CommonKeyPair(publicKey: publicKeyData.base64EncodedString(), privateKey: privateKeyData.base64EncodedString()) + } + + func getCrypto() throws -> Crypto { + guard let privateKey = getKey() else { + throw NSError(domain: NSOSStatusErrorDomain, code: Int(errSecItemNotFound), userInfo: nil) + } + + let publicKey = SecKeyCopyPublicKey(privateKey)! + let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil)! as Data + let privateKeyData = SecKeyCopyExternalRepresentation(privateKey, nil)! as Data + + UserDefaults.standard.setValue(publicKeyData.base64EncodedString(), forKey: "PublicKey") + + return Crypto() + } + + func getPublicKey() -> (any CommonPublicKey)? { + let savedPublicKey = UserDefaults.standard.string(forKey: "PublicKey") + if (savedPublicKey != nil) { + return CommonPublicKeyImpl(encoded: savedPublicKey!) + } + + guard let privateKey = getKey() else { return CommonPublicKeyImpl(encoded: "") } + guard let publicKey = SecKeyCopyPublicKey(privateKey) else { return CommonPublicKeyImpl(encoded: "") } + let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil)! as Data + let publicKeyString = publicKeyData.base64EncodedString() + UserDefaults.standard.setValue(publicKeyString, forKey: "PublicKey") + return CommonPublicKeyImpl(encoded: publicKeyString) + } + + + func removePublicKey() async throws { + UserDefaults.standard.removeObject(forKey: "PublicKey") + let query: [String: Any] = [ + kSecClass as String: kSecClassKey, + kSecAttrApplicationTag as String: tag, + kSecAttrKeyType as String: kSecAttrKeyTypeRSA + ] + let status = SecItemDelete(query as CFDictionary) + if status != errSecSuccess && status != errSecItemNotFound { + throw NSError(domain: NSOSStatusErrorDomain, code: Int(status), userInfo: nil) + } + } + + func getKey() -> SecKey? { + return key + } + +} diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift index db5219cba..2fe20dad9 100644 --- a/iosApp/iosApp/ContentView.swift +++ b/iosApp/iosApp/ContentView.swift @@ -8,13 +8,32 @@ import SwiftUI import shared +let biometricUtil = BiometricUtilIosImpl() struct ContentView: View { + let greet = Greeting().greet() + @State private var path = NavigationPath() + var body: some View { - Text(Greeting().greet()) - .padding() + ZStack { + ComposeViewController() + } + } +} + +struct ComposeViewController: UIViewControllerRepresentable { + @StateObject var biometricAuthorizationViewModel: BiometricAuthorizationViewModel = BiometricAuthorizationViewModel() + func makeUIViewController(context: Context) -> UIViewController { + return App_iosKt.MainViewController(bioMetricUtil: biometricUtil, biometricViewModel: biometricAuthorizationViewModel) + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) { } } -#Preview { - ContentView() +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } } + +extension BiometricAuthorizationViewModel: ObservableObject {} \ No newline at end of file diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist new file mode 100644 index 000000000..2a21373b3 --- /dev/null +++ b/iosApp/iosApp/Info.plist @@ -0,0 +1,50 @@ + + + + + NSFaceIDUsageDescription + Set Biometric 2FA + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/libs/cmp-mifos-passcode/build.gradle.kts b/libs/cmp-mifos-passcode/build.gradle.kts new file mode 100644 index 000000000..f90e37229 --- /dev/null +++ b/libs/cmp-mifos-passcode/build.gradle.kts @@ -0,0 +1,86 @@ +plugins { + alias(libs.plugins.kotlinMultiplatform) +// alias(libs.plugins.kotlinCocoapods) + alias(libs.plugins.android.library) + alias(libs.plugins.jetbrainsCompose) + alias(libs.plugins.compose.compiler) +} + +kotlin { + androidTarget { + compilations.all { + kotlinOptions { + jvmTarget = "11" + } + } + } + + jvm("desktop") + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).forEach { + it.binaries.framework { + baseName = "cmp-mifos-passcode" + isStatic = true + } + } + +// cocoapods { +// summary = "Some description for the Shared Module" +// homepage = "Link to the Shared Module homepage" +// version = "1.0" +// ios.deploymentTarget = "16.0" +// framework { +// baseName = "shared" +// isStatic = true +// } +// } + + sourceSets { + commonMain.dependencies { + implementation(libs.androidx.lifecycle.viewmodel.ktx) + implementation(compose.ui) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material3) + implementation(compose.components.resources) + implementation(libs.navigation.compose) + implementation(libs.multiplatform.settings.no.arg) + } + commonTest.dependencies { + implementation(libs.kotlin.test) + } + androidMain.dependencies { + implementation (libs.androidx.biometric) + } + } + tasks.register("testClasses") +} + +android { + namespace = "com.mifos.passcode" + compileSdk = 35 + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + sourceSets["main"].res.srcDirs("src/androidMain/res") + sourceSets["main"].resources.srcDirs("src/commonMain/resources") + defaultConfig { + minSdk = 24 + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} +dependencies { + implementation(libs.androidx.compose.ui.tooling.preview) + implementation(libs.androidx.compose.ui) +} + +compose.resources { + publicResClass = true + packageOfResClass = "com.mifos.passcode.resources" + generateResClass = always +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/shared.podspec b/libs/cmp-mifos-passcode/shared.podspec new file mode 100644 index 000000000..35c25eed8 --- /dev/null +++ b/libs/cmp-mifos-passcode/shared.podspec @@ -0,0 +1,54 @@ +Pod::Spec.new do |spec| + spec.name = 'shared' + spec.version = '1.0' + spec.homepage = 'Link to the Shared Module homepage' + spec.source = { :http=> ''} + spec.authors = '' + spec.license = '' + spec.summary = 'Some description for the Shared Module' + spec.vendored_frameworks = 'build/cocoapods/framework/shared.framework' + spec.libraries = 'c++' + spec.ios.deployment_target = '16.0' + + + if !Dir.exist?('build/cocoapods/framework/shared.framework') || Dir.empty?('build/cocoapods/framework/shared.framework') + raise " + + Kotlin framework 'shared' doesn't exist yet, so a proper Xcode project can't be generated. + 'pod install' should be executed after running ':generateDummyFramework' Gradle task: + + ./gradlew :shared:generateDummyFramework + + Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)" + end + + spec.xcconfig = { + 'ENABLE_USER_SCRIPT_SANDBOXING' => 'NO', + } + + spec.pod_target_xcconfig = { + 'KOTLIN_PROJECT_PATH' => ':shared', + 'PRODUCT_MODULE_NAME' => 'shared', + } + + spec.script_phases = [ + { + :name => 'Build shared', + :execution_position => :before_compile, + :shell_path => '/bin/sh', + :script => <<-SCRIPT + if [ "YES" = "$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED" ]; then + echo "Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \"YES\"" + exit 0 + fi + set -ev + REPO_ROOT="$PODS_TARGET_SRCROOT" + "$REPO_ROOT/../gradlew" -p "$REPO_ROOT" $KOTLIN_PROJECT_PATH:syncFramework \ + -Pkotlin.native.cocoapods.platform=$PLATFORM_NAME \ + -Pkotlin.native.cocoapods.archs="$ARCHS" \ + -Pkotlin.native.cocoapods.configuration="$CONFIGURATION" + SCRIPT + } + ] + spec.resources = ['build/compose/cocoapods/compose-resources'] +end \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/BiometricUtilAndroidImpl.kt b/libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/BiometricUtilAndroidImpl.kt new file mode 100644 index 000000000..1cadb17e0 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/BiometricUtilAndroidImpl.kt @@ -0,0 +1,132 @@ +package com.mifos.passcode + +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.biometric.BiometricManager +import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG +import androidx.biometric.BiometricPrompt +import androidx.core.content.ContextCompat +import androidx.fragment.app.FragmentActivity +import com.mifos.passcode.utility.AuthenticationResult +import com.mifos.passcode.utility.BioMetricUtil +import java.util.Base64 +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine + +class BiometricUtilAndroidImpl( + private val activity: FragmentActivity, + private val cipherUtil: com.mifos.passcode.ICipherUtil +) : BioMetricUtil { + + private val executor = ContextCompat.getMainExecutor(activity) + private var promptInfo: BiometricPrompt.PromptInfo? = null + private var biometricPrompt: BiometricPrompt? = null + + @RequiresApi(Build.VERSION_CODES.O) + override suspend fun setAndReturnPublicKey(): String? { + val authenticateResult = authenticate() + return when (authenticateResult) { + is AuthenticationResult.Success -> generatePublicKey() + else -> null + } + } + + override fun canAuthenticate(): Boolean { + return BiometricManager.from(activity).canAuthenticate(BIOMETRIC_STRONG) == BiometricManager.BIOMETRIC_SUCCESS + } + + @RequiresApi(Build.VERSION_CODES.O) + override fun generatePublicKey(): String? { + return cipherUtil.generateKeyPair().public?.encoded?.toBase64Encoded()?.toPemFormat()?.toBase64Encoded() + } + + @RequiresApi(Build.VERSION_CODES.O) + override fun getPublicKey(): String? { + return cipherUtil.getPublicKey()?.encoded?.toBase64Encoded()?.toPemFormat()?.toBase64Encoded() + } + + override fun isValidCrypto(): Boolean { + return try { + cipherUtil.getCrypto() + true + } catch (e: Exception){ + false + } + } + + override suspend fun authenticate(): AuthenticationResult = suspendCoroutine { continuation -> + + biometricPrompt = BiometricPrompt(activity, executor, object : + BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationFailed() { + super.onAuthenticationFailed() + } + + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + super.onAuthenticationError(errorCode, errString) + when (errorCode) { + BiometricPrompt.ERROR_LOCKOUT, BiometricPrompt.ERROR_LOCKOUT_PERMANENT -> continuation.resume( + AuthenticationResult.AttemptExhausted) + BiometricPrompt.ERROR_NEGATIVE_BUTTON -> continuation.resume( + AuthenticationResult.NegativeButtonClick) + else -> continuation.resume(AuthenticationResult.Error(errString.toString())) + } + } + + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + super.onAuthenticationSucceeded(result) + continuation.resume(AuthenticationResult.Success) + } + }) + + promptInfo?.let { + biometricPrompt?.authenticate(it, cipherUtil.getCrypto()) + } + } + + @RequiresApi(Build.VERSION_CODES.O) + override fun signUserId(ucc: String): String { + cipherUtil.getCrypto().signature?.update(ucc.toByteArray()) + return cipherUtil.getCrypto().signature?.sign()?.toBase64Encoded() ?: "" + } + + @RequiresApi(Build.VERSION_CODES.O) + override fun isBiometricSet(): Boolean { + return !getPublicKey().isNullOrEmpty() && isValidCrypto() + } + + fun preparePrompt( + title: String, + subtitle: String, + description: String, + ): BioMetricUtil { + promptInfo = BiometricPrompt.PromptInfo.Builder() + .setTitle(title) + .setSubtitle(subtitle) + .setDescription(description) + .setNegativeButtonText("Cancel") + .setAllowedAuthenticators(BIOMETRIC_STRONG) + .build() + return this + } +} + +@RequiresApi(Build.VERSION_CODES.O) +fun ByteArray.toBase64Encoded(): String? { + return Base64.getEncoder().encodeToString(this) +} + +@RequiresApi(Build.VERSION_CODES.O) +fun String.toBase64Encoded(): String? { + return Base64.getEncoder().encodeToString(this.toByteArray()) +} + +private fun String.toPemFormat(): String { + val stringBuilder = StringBuilder() + stringBuilder.append("-----BEGIN RSA PUBLIC KEY-----").append("\n") + chunked(64).forEach { + stringBuilder.append(it).append("\n") + } + stringBuilder.append("-----END RSA PUBLIC KEY-----") + return stringBuilder.toString() +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/CipherUtilImpl.android.kt b/libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/CipherUtilImpl.android.kt new file mode 100644 index 000000000..d0b9a382d --- /dev/null +++ b/libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/CipherUtilImpl.android.kt @@ -0,0 +1,61 @@ +package com.mifos.passcode + +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import androidx.biometric.BiometricPrompt +import com.mifos.passcode.ICipherUtil +import java.security.KeyPair +import java.security.KeyPairGenerator +import java.security.KeyStore +import java.security.PrivateKey +import java.security.PublicKey +import java.security.Signature + +class CipherUtilAndroidImpl: com.mifos.passcode.ICipherUtil { + private val KEY_NAME = "biometric_key" + + override fun generateKeyPair(): KeyPair { + val keyPairGenerator: KeyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore") + val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(KEY_NAME, + KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY).run { + setDigests(KeyProperties.DIGEST_SHA256) + setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) + build() + } + keyPairGenerator.initialize(parameterSpec) + return keyPairGenerator.genKeyPair() + } + + override fun getPublicKey(): PublicKey? = getKeyPair()?.public + + private fun getKeyPair(): KeyPair? { + val keyStore = KeyStore.getInstance("AndroidKeyStore") + keyStore.load(null) + keyStore?.getCertificate(KEY_NAME).let { return KeyPair(it?.publicKey, null) } + } + + override fun getCrypto(): Crypto { + val signature = Signature.getInstance("SHA256withRSA") + val keyStore: KeyStore = KeyStore.getInstance("AndroidKeyStore") + keyStore.load(null) + val key: PrivateKey = if(keyStore.containsAlias(KEY_NAME)) + keyStore.getKey(KEY_NAME, null) as PrivateKey + else + generateKeyPair().private + signature.initSign(key) + return BiometricPrompt.CryptoObject(signature) + } + + override suspend fun removePublicKey() { + val keyStore = KeyStore.getInstance("AndroidKeyStore") + keyStore.load(null) + keyStore?.deleteEntry(KEY_NAME) + } + +} + +actual typealias CommonKeyPair = KeyPair + +actual typealias CommonPublicKey = PublicKey + +actual typealias Crypto = BiometricPrompt.CryptoObject \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/Platform.android.kt b/libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/Platform.android.kt new file mode 100644 index 000000000..c91875625 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/androidMain/kotlin/com/mifos/passcode/Platform.android.kt @@ -0,0 +1,10 @@ +package com.mifos.passcode + +import com.mifos.passcode.Platform + +class AndroidPlatform : com.mifos.passcode.Platform { +// override val name: String = "Android ${android.os.Build.VERSION.SDK_INT}" + override val name: String = "Android" +} + +actual fun getPlatform(): com.mifos.passcode.Platform = AndroidPlatform() \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/appleMain/kotlin/com/mifos/passcode/getPlatform.kt b/libs/cmp-mifos-passcode/src/appleMain/kotlin/com/mifos/passcode/getPlatform.kt new file mode 100644 index 000000000..650e482c6 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/appleMain/kotlin/com/mifos/passcode/getPlatform.kt @@ -0,0 +1,2 @@ +package com.mifos.passcode + diff --git a/libs/cmp-mifos-passcode/src/commonMain/composeResources/drawable/ic_delete.xml b/libs/cmp-mifos-passcode/src/commonMain/composeResources/drawable/ic_delete.xml new file mode 100644 index 000000000..4069f8377 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/composeResources/drawable/ic_delete.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/libs/cmp-mifos-passcode/src/commonMain/composeResources/drawable/mifos_logo.jpg b/libs/cmp-mifos-passcode/src/commonMain/composeResources/drawable/mifos_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a067a3c8edb0f19925c7f9ad7c5a72b27b5e5150 GIT binary patch literal 75684 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%0A9SW z+j{l&{=up@AD3me+DaDaEYwdFiOh3kV^$x{6*C=2OcxNdb;FJSo0=MN)E%F2?Y? z>D}u$w|Z!*#AniNRo8tr1k6m8?=sa?lWso5=GxY>AZe(i$89Co^4McLym!)+^)05K z61R!2B4Pji327>{*gfuEzMqFJ1pRQL9X_tCk{0Q%Yo5+p~pfm1$$?v@aJ@yV&E8W?njmzQMyLko$a{R7!Ws zSVT^bQ;8Z!ocU_~@_2Wm%MBG~10C~6?FRaWqlUwsJHv*C23L26hV}b6h2r(~HJBwE zh`L+jTsLSapNyq_Oum$p&XUfu*HX8Tvg}_9hdJ=DcyxEG+1d)*T6=&$f~Bd}r(maW zGhsdBp;-~_*bwy`ca5V|e@DXG!<^#rhB%7%@oIG`&6rTN`UH%yWye0rtB=8B3*>c%yC8^-A8yw!?x|Ia7oJTIF6)HOIh3x8H11X zfY|4L*_?p#fb0d?6j$w=PsP%rn$Fq^8}bSU`{io4N%N2MU#(1XL675~7F+7pmxlC5 zmcWXKR%nO}FZNzqH(Pkv9I&|tFU0ubo|E1t`vE#1a%Z8PY^AuV?R3>&bV@e+qj*@$ z*x~GA7P;$IV6qqjAWeKJNJq9!$f{~QZjDWrvFbTu^3$=PAGR5uk`nUOU|Z1Fuu9j5 z+*0?XV=AqVZLQJuNIv;^<$iGtt8Rtl&@e6rC*AB*WiM5(nmyr(&$Rk((uCLPt#7`x zh^8lQ;=uk%F4Urt7PRHMj_xBx_LzpK+iivRO)`5b+ht?lp1z)>%^alXI1|}3?3*MUh@?rM zZymMfc#(EDIx^M~R`7o9d6&lBJYo04C(O~8nc{R+q1ujS18L5O-d<-*!@t`Eh`~Q! z!2jk8s6;)l)uWmGO<{z7iMm$6o7fNrD{gZ(kGiv|)!;ktk%Tp$5_UmNs$2;}maejO zZ*QkUw_k+=!#2s7@o~`FOwCE#`i+C*49h!MbCCSBWv1KM&+Hh8ObXsKbx(gIb&EBQNNhy+dymB|tK?62j>Tj@a(Rg!Z-S*hE z0ZzW=0CTz=sBTZ0BWcAr!`-6#3+d0yCq+is-_#$ zw_?epGE~&|X}adk=_As6#vPvHZ~z}xE9UQios!=PeT>=unN+37;fc0}X0{%so_thi z(5JS>=^0URkA)4qn#pE9V+_BykyYhSJ4nUiu8dN2jMVGH(^_@u9<&(+slxj`-f_fmDG z-fVWF0%eJ6Kpu)R4TBPkhhy5?*1LLm3eVxQ$dz1ZXf`L7Y*c&a73drJ$WZPV z6U^<04yT3a9J`0aU@ExrNt%7=mPqdQkS29dWs+agFPpqTW3fknP zvyQ7>XbN{huZ<*^(Mjv&4uW?saKA8lcA?^vYf?haS>*=trSQQ1gZ$~bmgPgwM=351 zNq7beo#xleP#_pDP5CU*)i(cvjfRVYP^bliS5&rQ;iwIA{N*MESXBx&O0a+DxW1% z@-5^jtn#z-Be9smlj4#{!YQm2L(@~z(XeTBly5`BDWKYY zSmImbS4OQ3bXoM3E>J}Rd;pz7J0NRks(=9a=;QX9FUzRAKAcrgOlh!m>0H{kGuADs zFW+>*CE2+4#8E6FM`pF1WlWs5jFFU`QiAEB5#?c-zsrVy-AfZr`v^s7m;C4KQ}N

>gm2ISlLM9*7;ee6nI9jREhk9EJP%Zv4L&%;OddD) zY-&jUw9)0dp{e&$#7JjA&SD$^(AVyNYf-;FeS832>&Pm~?OB`ZP|)2IyqxbCAd-LI zR%mnJy%YTMP@mg!KI!hF=yV20NFx6JH{a@D4x8h!idyCIaX;GJnwlGI1f$1&r;l+p zXU9;|rO4Fr^mt8m)?UF6vo@u)3_gX28pPQv7ks@uk-hv}Vo#1_(f!-paL~qfa)U(e zm)iEA#wHO=jhjAiG#-5G8IdkK5jw#5HbsdmEkn&Z9WX83*JqFDMXj^N%>>M4oZ-<$y2z%cKcqz(BHWcBJetjhDn zT2IuA%|YABI&qaaJ*rn;yMC+Qe6YA?!6)r~q^zVI!Cy75Z?)d4jIq1k&~;oU>>B9H zyPA~eGO&E*UUo#Gt?XSwIbY~q11>iSrg^x{*r9^etY`3Y`(8vyCPO@?Kibv zTNl_?e{;gWJ1~}i9KXw+(;yTG)b=NLja13^jDI6vz?S}nC;P^O+E%Ujz|qg-A|PKlf2?wrR+ONt~tKl znVQc9zk>dRlO~}s64vcec4YYN7PBtd)mv?uZM8H9^?|Ov)G8t_ij}W-lO#T9pjsQJ zo=Tjsgk`=JLn~NW%p9a`8%;w1Ba#ZW^<{2)aPyePaz5CLLkgUdey4ha&)W%^>DS<{ z1jaEMW@@atd4>1q+`VNE$-8ehHf-nZz4SOcwEwW~GeGJsMUc&YqWby41HMiDr3UTU(5?x)Rum1{;i5y~_wMPi>z9~yXx8=`BfC{28cXfxi~GTO&@myMlqpQU(G zjLP^_eCWyz=OhV=Hp)b8Q{$nK#2D?g=-{_6hL&NdZiqM0Lm(M0~pD^p3 zJrJviS@Vk-IvbqG8~cn=S8Fl!xn*=-CS>;5ZxkDi2#X=ingES(4=u#P588qmBNv=B zfm&9}YqO3M)L3KxP;n#g>Uf7RD+b5C`kcf=oLeP~q0-lZ3+>YhhSr$nvg{t=2fYoi z%w#&8Z|KRdyVIzdlE=PE2!K7_FW|beq*Ps1tBaLonj!cFvv7I*D8HblO>Y)MmVjvZ zYf-GGo!gLn)T540{AbDNJ=|#AXO8IB#e3GhCVg^sm=irQ6Q?gsUCO_u-U-_)rGT*H z5B6PyZobFEBr>^gEl^nz*(-gSV4{P0y?PghOAz04%T zf^ampxy08(I|I+iJ+KUfgfNcpuN!e3L(63jyASH+3L;6ewIpww6EZS2nWjZ%pgh~%gv;ItMuoq`aUoyzU{{Su4nE6W0n*a`LE4T6M|K-_y@Z1M zES5bqgoCdYIS0#}rSrd*Iqg@vRIALvw+pt`)2_tnwW-d{w&EKKvsjYxoKAU!OvU)s zWi5}p4oIdT2f zFc9W*Gjejk*8XeKrRXC3x-s3+wY6MzrgoDAqxlTpSQYaN(%dlda-0(Xx`Uzx`WbKy zf_p;U3@?io4K>W2&xBQWP4Df42TL{~x)RI0Qg$RH$VG9aTELz z)A6mL5(8`~y0;SlOV*&-7|C@6Q18Qen_721WKc_|z?Z(mN#V=80O{k=!1%M;8~v`F zcJA51Q!7wc1!WzMjDiF`=9XJxM)YW9WOB^U`K9MFuB-gEcz|5DT1EiPi3>Xdg%bv5 z&^gg|siA5!$BL{7S8pw-O`e0@gs?D?ib~{|WNE4H4UI~n(noW}Pval5T0TBakH}7} zV|zMI76zNjDPg%Asqjq;cn}iNVYay`UB%f?P@gd!XLN9E62SAY&De+4Iwqy^qm>z~12bQd)u9gEkcl9$l#*BAbOfdVR51jmerE-Jnd^ zPGtN#EDUdxbl(M`Mq`32Z+XZ6YSj%f{O9BO?>?T9{-1I~9aElU<>%!`sx5P{#WIFs zF)FZBYH;0!MqbX=?e4y!&Et0fGxX6{V(oZ_$`bz?GOuOetJFCqZlnnTS{e4cz#J+~mJ_SM(2cdDM+WDP95ie8}z_-om<*zU|=raND9)3yA| zc*Fe7QqQ>%E1PGM3#ivPIG1Y-VRKhiCCMG3cQQ4c2~|i;Ozjhz%dvxHO>8r;N@-g4$){S zzjwy3cc0bWciIAgL}i-?I4Fy0t_GF0(Zj3U#kB|T+a@WV4_ApAa>R(%`jQbGvGl0ptLQnGmk*>o7;&Q?a8Rm#CfNSu*{`~85m~W5HKXv-hvnsow2V)6 zb7R6VeXV+QVnWv?UwW;JdO8O%)ugnm@!FlA?)pqu_0kos zHx#oht@=g!=(N51gvRmH^!|swEYeT6YP%8 zQk5NCWkYn@U)FDYaY>r7`r3dV`c6QLJhqRLHJE3iI~^r5Q=cPReJ5XXhxaA*S|8tz z^5;?VZA*N@*09-VmRTC7FBod9k1H$rQ5qv9uy+}}WzB7mL#iZJihMrMPvdvaP#am# zrp*Z`;j4JhFTO~q7Ayd_Yh21rs#0U;%7hxS+`>ke+~08NI=-~gG#&CX$!DPVEZ17u z1GD?K+zxYg^o=oiU3~DEe?Z%5cT+o3QS$9XD&kb$Y;}N~jFbW0Ah@GtB|lHG;R2 zD`gBsuR~XQls_(>HmcNE=SS+WQoG{4!4sLC8s3`N9cJIVeP3pldl7(daoWCpA`4ewsf!X3=hLrg?xb%zMzfTKA48mi+st8Y|HRWz1Y+dR zr|jQ-%2YG@EWQSMsk|c`qPDxV8x*#!T-t0Grg8b|s}K|G?A(IYtDg;`m~5>|bKWk& zs;Uk-LQAN4{W%XTzsU8_9GqG)E@D^R%|KhJ}qGX23#Ju;W>_@p$#d z<<2s$Hz}c=8H-!x&(Wd{sOX3!G0eGW(%OP{x=NFF(QiJUG9Ro65G=2iZHsPxzaQdm z{erl!q6TipNyibx7;CHgrRb93U5|>-4r$BrG;Fyyqu#Q|`K2paUjEWWcYU%h!@gpE zYHYPGKZqf=PL0QywGAq@qSJdDPouxI@AsO?nl=m`kE!TI^*{xxbg9Jo z1g$upaoq_p-!jG~EEv0K z-Mdl$;7;6^B$oIaFN=E`+5}uD+Zy0cr=&Kzb+1j!KFFmt*UZ4pL~S7)lz&gEXo-#k z>a-yCf2+GZ#Fz$r94#;b@O5rC*} zxgF=W{+jExmkp;^4jyI%!0k@k*0R?wcBnUwd}YkFzj?XuAtyni4zp`QBaC;7JOSJb zb6k)*)IZW^R}~i!t8M$pkC6RvK#djrS2bn@K6D3f_wwFOH{O5wh2PSLXSi>DwS|+T z-F+gE+#sUmcHKb#%G+HNIApjrn97%*oT2|MYUUovd}J);EczJ^|Mnsj{w# z1p&B*M$_8?r~A@g3e>8vZ?>C4=2dC!@tQ@GoXUNhHfN6GLh2@|XFL~tZQ+4w0x(g0 z&0_BB?F36P>y95UA8~xd$9$zl7puGqdemc z=R&->Tk}WM(sBZBR`WmGrdVQMlU1Tcit*>T_1B&cg@bozr;`?xfo)a?WK-KiAwCtw zbe~$<7!@bYR9IxXJS|}uiuW3QIE;kWF6D%lGp4HX6WC5%S`q208(+V;px{gJSs~n_ z+ih1!##}XhI546v(5&xib8o7P*^+Hef0&$b!+sxOJE>pC2Mu{u!o{o6wPj_Ft^KTprGbzFU{VS0=&%{SmwFhW+jOMApeId_>AYI_=1L*FpTq=*6jtH{S50 zXR!+GduaOJGv-l&6aMkA@wzbEI${^2nk6AkF(Hr3Fj;1WM;>|4zR)Go%`A@o>s^0{ z(LaOsztDxB%8`??bN1F3kG`kq8fecFc8!{pR!``sF&Ze*bji_;wcOWSduLENO*T0c zxWQ_!qYvTTH>gq^qY|dQd|WuWy_-Iv=2Wc@-D-R!DwZmtC@WAwssX z#=TY@uwZFf>?^W6T{r17F(F11P;6kceR%awZce^YY<6fmw`-9}u2(o#VovNZ>0|Qs zhSHhxwc3gID_$?wYWjuZB;E9Nbc57(0iyS}P*iCtosX5jHm|1!cU6uH9p?@e)993u z`3iyO8N16F^$i~1!d_WqJb5L9>%0+~MjZXo3r`_+wXyed)lSO@_^a1z`?f;%Au4>Z=eySrzS%Eb>_^}xpU^$t@>`&{rlG2(EZ5X>sfoR_0*EMtQQMmR-zK+0xKy^ zpr=Iq2OC1-tX~CgcN2?>LLu)piv>ON!@tN9pLh{Jmr`NcD`rmgQ9W3G=Tqe3Z=3%z zO{9^xDTN>fx6wK#3&Yx2hC|}Y2}M@<$yNX8dUe2mblz_5j?k-Rp>d}+YRdHaUT%B? z&5k7C`<|G(;vdNu!scn~ZM}Dym%wRF{kdH|n0%vqnU*lV_c16eC6u!h7%7IRB!g^ z?(??2&h~BiOmc&PlTK9YXM%Q_1z+X)3@om`tvNs`?#G%56YrP-P^G>vt!6r+&7K>?D&D#7zp= ze!w;J&g*a<;fY+kl2^2v-3(vWQma?epEr=8_-(~QIZysM9N(L{M^a;UzwjOa=?PdB z)Kk5kyBJ!aY*1BLErTmg7r@TqG8FV-`)s&Q>)RBLVf7w~`ZOHpk(nc{UVevs%aCnF z{aJ#2;;66~uESE7~LJ@WzEbQAakIY~Eb zO~gpP_Vkg^f+BD0;gKaDKEscrttqK#ht_Zo~& zEU?i1xcV4b%7Te;oz_yb=%3@zFrVu%8Cc74B`xsimUX&q_nV&1b(qh$>+N{H5&Ode z2x5#c${wq)w!v6SW0SbMYJqt@>rqJ`is3xN@1N#Uga4=b_^0FYw;9u;9z?BzNQTNI z8*Ved=jBn>R#FxCo*`9?V;UCDxSi?kCo>?aVlP>J)+Ll>D8CX+-9qF;ugy>#KFo+u z88DZ;vQ;Nq|JBA^K5MQkTPG$eI~?BI(VNUENsudH86`!R-1u zNU<>NM0TcPauh$sCAC@k$>sk69<6(3_~r;UWcz?5wFBburfc^(o}mc^^|6kAV_e%^hm*udwPa| z+k>D|T^)n7DwVTB0OPqhCe3=G{=#nA-&!LbGBR(Uf{yIuPTg7~r>kJ1^9|a<^#9$1@M1dvZLv zrW3M?Cr$L_#$?#`Iqrvy7{3X>G;*r>jIC2*2%k^y_bMsU@VRb_Un{6bPd&eLVi7)P z@7Q)29Ev#d_{G%%Z8QnyYGBC~3MfVLE&A1(BbTNe<>{XZ_ZFY17`>0_K2H&Bp;H~} z{PSp17K8J*|ID>pqfg2ZZ`0$j_j85EZ_V>Qy$j;u`{m9pGjl9}G4_aa!=Ooj3obh4 z9JT!39w6`A(@`5#!!<0<()x?&;Yr)-hYuxHtiCrw1Iw`bLyeAMzZ?(e85~xG@rwcw=3MoG!{yNl;ArWz3|_%p?POlJ`2t8_#l*NDzIM<3gD| za>PTZhs9o>umz)%e+FX@@8baF2Wz!mGuAht8o@mtSk9{SY2yX#M9H|$z)@cEco|yk z%N;vzA6a8J?UUc|1A?5sD$$nn6#=Og?Pd+K)9xqoGw-pQ(p~=sI{U+Q*ZW#UHwXG* zZ5$8<6NBUAq&reTkt61Mgi;pU?yLPA{TaXAOWF+4Oq(q!y`<#%pt$-gEuA}Jfvk_f zr(2`eAmvT_GF34s5^?lrgBP*$XVpV$erP=Pa~Y0U)UTnHlQYg;_xxTH&Ucfe8slgz z9!LADVtq+yesReTrL3>H(D!&l0pw~fEk$MBFTs2ngs=79%fhTdTfr>AhPW%TL^@hh zBWm)Jvx4RK)pKbzo0Eadr@mGQu{E8{b)-tkdf8P`qIUJSU-@ZH=ZbP(Y$qRK>e*$u z8Pg|?^SoVCdBiN$R=?uMa~K-%4}rtFEh}>_x;Ra6Yru4;v}?{x7ofD}^!Ry`S3jqB z7l-5B&d;d9R*`!Eqb~12(|g8`!Sp%Qz^5@|8oiyXFY{x00oC|H_W+80QAS^tUBuS` z(CUd$u*pglUL&U_Nl_YM%py71(F~>N@9U>v_foL2ER55u=uMDpR=89UcO!eycZ&st z12|J)KiMn)>4v;~iblIemRUIeI8z5eOMLuG1+JuQ-}>Y>T#Q~Gz71Wd?dN+Tg-VR& z3m==zk*5Q*g~+p1>M4FqRB{ar>?jVo^S!=F5fyPWp7C3MG6Lgb_|@D!r(YTCi#0d&%%AQxbAB zWWsdf)~H8NbPp&D!~yTJ%uw1x4(S9De1D7lf0w-fuaQJyv!J`0@fWM8{jie`(CCw{X0Qs!Df+U_7;<2cK z&=mYfmK3U_(b9@fFM(R>)KJ9ItMh$raDkM;8sBN(^6kvxt;GZ-c#K>$8R>u8Zrjg3 zQsp6KL-dYEYyQHr$Y!rXYhJJGM*ArhaN!dq&-huE=eVoV1MTyQMO!a;E9C#IU>LN0 zs(1-%Ze=_7P_bxSXQq61K7@KkmaDAxl3NS%tJcYjEq}XXVf`IvsbStn$ zY983Z-b~OO-@T$#rbR=QTm$8d75%brDnl;ZP*Ydml2gTq1wDdp1P;Dzoh( z30q+Qw)jkOUSJ?8?;b$Zpm@>ucqPS>K1j~d6Goi%EYDOsr$`X3kQ4!K&S_OcMRm4% zl#TTD(wa(k+a~G=sj+Bn4spMZj*h#NNl;^jED^^^Ka>4JMbR9=_40c9{TUac199LJ z^@9rLfZ|@T8@(m0yql(f9MuvKO@jrVrcqPvAZ{0H0au;aC84Nxoi`!w14-2S;bK)O z-<6$!D=FV!cz>24yC_hs^*?1SrTS|GfZ}hG+_YKyTcG0XA*k@!J5jgCZchv<=nA7h zPPeuT#Bl)r_McQM zV0JNF{i^4dAPTqG01pleg9U^Qc_`c-$N(=ZcLZW>tW#EHil|NTh`tIWR*%Kk8Mmlz zvM_?{c5f~SQ0ULhT?;)jNF6d`rK0esc7QM7Vbs>1*K(K!fhi+6ZR7*@7&BHe6 zn*b6=H%!iqOkBp_Y%Wj>+=*}U{Zvt3ze+Rr+nU}TDpxKel&D=V<`CWJSFwJdzrDmm z0MRq5^fV$mOHF519?|eiL-tR=s8oaP4a?hf$8{Q{++;sX6@b4zg#+KX^?lHdu0K2s zXq?H|KTYV~D(xGnYs{|tzWy0BSjz8NO#r!>w2aP`!YX%go>5x&F!YD0L#RfM{Y)G;-sFMth}5C^^YZ$P4rj)C zb9q>Vgh?B(fBphXb>@Yusb$huCzkNnFD6AWm{_w|z5e{uQugvEs{&QQAyl<~@w7xIsd}G%Dg0ngf zZqFkFok*=44?=X+&PHb>(cn0xy-RU>>BV&*%5{5KD(KWTh#&RVAcSIBL;SNrx{B=flg*My0Q#YgYk{sF~9}1sDmY!t8jEPIRq5*3DF(? zt;7`tIFD^1MsrIS7RMbM=O6~v2JfvTX9TYO>vx`z5!s{>L%t_`BzSsuzC>!5xKu#u z?Ysg=?+A;eQuRkFb$Yy!DutOOcRzRniu~P4$oQ+5>JM|ri=|j(e-(}>1M4&Td@K;T zql>3dcKYdcqBl(2TJ+(jkzD*r$ATaUp$ZM$T4Sq>;zP7XJC*~q>AzM|=#%Mf+Xqvt zG=_Fl6So|v4)sq+=!M&5#%1TXJ=h?wV!GN#K5ap^b}J}A5u8$f6qwXzQbA&wngv4! zj-o0}$MhRZ9<7xz8`5qR9{XuGH zd~ak@JnFNL`To-jUz}J%f=y7PCNZ#A2!Ge>42UZD15OqR*t8os9;lsu%DxTx4cCUR zMg=MyoSGtoww;cpo$M5tbYeK1-%>YhMjO?XM^%rAZx!qMPfm(uDh*#AFgD*bl_E0c z?bmqL#y-qE6*Yt3s6E=!&V*+7By?;Zf#OW6J)5J+U*liS<3jKhMXNYe84)T|UAVjZ z9|e?qX^#)k(;XHr7ozL+T>SQ1VI89X)FB;G4yO;=^vn(K?SQKTz;hEGjjl<5rDl0} zcoa;JRmN3EK&o7pp&6;nj2Rkyuiu2zThZCzXJK5=s&ujh0Tm^Q;$%XPAi<|!UGHxBf7B%q;BpJj=bMJs(LNB%t@5sA{?_2c%GD&k3394#${=v?dmLS{+z zkCiuQ=Cj*FT*OQ*BY91y35t#NZ~m7O@M*!Fu}`SoSlWft_;rx46ebRaTFY)jCGm?t z2ATacV>MN7QS`1azNEPaQ)`h9eihs+o*fgA+1;ZpbR#*YD(o33CfZpFfrH~HgCqB! zrOjR9Z8lXo^{GwPK=62;v63hrOKYW#n3d24Q-*XXbMMd<7RD0l>~SukbIka#ubdxZLUej%iKu_Y+=r;?){mUkV`=i zBpVq|ny^%V6a865s-25fS(5FGZYRWT%sYsA`fMAlLw@a0@;7PLKVLBao31e0{Q;s1 zWAqx?MN( zD!<_7a)!OF8Pm81d&*;AL8;y*5 za(y_fW1hf+4hp>h(;cKXIFxy^fvw_V6hN!HlK79rAYDK4W}~sm#5pP_8Of z*tzS?XmBT|U}iGSc{b*$*q!$E$jY|q6hfq0B4)(bewnm5XU3=D^ZB@)c4|DkvD@@% zk>QBK&+ZRpOvPq`oRdyo%FV}95}3-jKTz@ZC_c1B7*Z|br=1D4@=KDghrxTVsVuuw zC(RiJWyY1$;?TMw>V|98R#_h-oS{Av=gj;xHe-f@-F#6Mrm|4}lqXqkVZ^KvrC0mz z$U`IBva(svJ}#l20EDgR1T1;yJS3Af4H6ofQf&4q9&Twh@4)`)K-l$&ws+&rd%`E@ zO|!P-ikmHn`0vPl8GSACF{PK>RJK-<`fQyK;sn1U*^`{MM7C{adB5W}rEl!w+Uz0z zNCM-+r;=CIUh-p>=U8->M+q^mNsFV)_)-Vn+D5?I<9G56E=uhtMN8!Kt*^`%ecIlf zkYvdHoL1tJiXLRPdP!z^)`4N!9JsyYve0my3e`7XSgaj+P4J69oS07X#ijDg1R9U` z7Ch#3$ZA-b)O<_hCSGO&Z{dt&vtj>=xz+3=wxKQg7EAp0KqY0+hwPE<%hMQmRBgnC z>Wr2j>Efz<^kVv|j)c;#D2El>@id%0f-WW#R=CLHh#TemDK|p=F=vIR%}<}c<;H?K z106512J)5`JG;5;D%^MD<8dRIOo2zE8#WeiqMq*)-d#}b`#p}Nnz4m)H#LA#FMn*l zF0?c(JpBQFAs)P1YB*&kVy$;5BDn4G58G8b;?feH(48?^aSH(tmSo)#@xN zNz)@N?IS1gwNucEO-s&1X%JB`sn3wl8P(M3!BtU-U7{%W-K;0~&G~qSukckuOH}3U zIvQF*-LGs4TtoGV@BTDf-|17@Y|34_ovEUF%bFyo2siElKQp9F+`&c%kp1!cgQey9 z>uYy-v$~BXqVIT4Y~@0v!yqCnqnbLgdt;}n_GC<@MCN#M?9<5$5P^(x`pY(#G{4h? zOCRq!uW=bZoRX&uo(P&}8FZDj6&g`vu@dhAM6qA~(1mBH7zB7pZnL^9^Plu^U7O%d zj~F#A!X$a}VA34Hk2_uTgMdRJt*IA+xl~K?VFr6Xzw>R?RF9Z z%3n@XuTib(e7M62Q&gA;52D{1R~u5y#qW6Dx|U&dU3lGAx$h%VHRJNdpH*wR<{psh z@|_|xjy1E!qBL-0`S8(o{i(*cmz%B22Of484sZimQIoD3K4qqLsn~VQ4H-(xp`=9- zvDvZT*Elte*Pf{fQsI=)rXy1C90=OmMq1KyV82c%J6UGT{!+kT)rXbObS0)5^{x4Xq2Osh@80M$$w#OR;fmdKrAzLSB5_vk5Bw z75ImEM*&H?<22zfK@c|ZG>JK~XCv|95cURBQ3(cp`?vSl-*X9)usLk>I~K{fy=^{N z?XiyALY9dn zJA&S6X%3g2nP>R=ciVQEz`EABi$2Rn{Li z_LP0%po}Q)1Q$A(JaMdKZB$Xbeb5x|Dim-cEHz4 zfvCCT77i(V`u*Z5=5qZxqSYSOcjA*!yaq%K{{x|ZnQ z02}>`<*@pr$sXb?_*9)ZMYcni#ma05I5!c_Bv=>Vy8q8>X8-!{{1=gqwbRYfbc??M zTtaiEz&~^j5>67in?*iR^+K}BJL$HcUAFqFp>1~p@f5|SaUO)HE1JNJw28l|-H&Mt zc;G0qIgi_OHo^h(^cBS~YAMIx-Cxh_Pge!kAC|K2-L@rQo)X^efDHZFJwokaz}Koz zSPS_w6de@j`qULGRct9b?_Mnbc6L*RQL3^fW+jKai`gHiT4}@zH0{o_gI$u&uyPdx zu!NuA13qPb&HTC^Najt7`gGmrzm?>G%*;4!Tl^GB3oFyT2N)h$*2R$pm>vX3&fdjS zwikwC>ukuE*kG$KHg;g3Js-KN1YpVrS&}79%XT z?9k>w#>=*}4fFOU%yjlFuc)_5^CL@hInj#1cNnTOzn3>op{?XM6$f{SvW2szeJkVR z9P;BnR#5ad%+bEE>bd<@%TiEW>bL8zubn%cV^b>Cl6tZ_szO}~v~0{V{=0swZ*My{ z6yg)Jt88>oemWsy)iIrFop1Ua@aC%;Yq2@sggW9-*doD*84Gx3y$*^;-XH4YYE@u2 zbzAxo4HtMHUVQ%%LW%W)wkhG`#|ZUHk|iPebpvUnftQ090CwwAyI) zCG$Rw*GwuBA-$=0nu=1t2M|_4t)4<~pikFTZ&Z5KdG6w|*UQtOw#d*UMC;(PNOjwZ zvl$9(WN%xxJ2KrQV{*NaCqK;0v>v;`T|a31n7N2XH9I4bY3&06(X);NA%^7vpSN=r zaHYSXDRnw(dZMCpw8w-!TW4jTh!brx6h4s3w+Ksyh7G2rZyd5kWrht|)@X7|mty0==kwNrR&BQ#(Hib0QnL}n;#j(dED2A2wU=2O|3)~UZlYp- zg|L&nm9Z3|F01zsM=#VOJbQH3PDn2dDCYZB!XSxd&~6-o|r_TRkqRP91-G|q3)Wr;)I^g2C(8x@_8BbNdSA8!6&7Lg>0 zETeROa6i*PG5S?RdD4;0yzfE2nieO=k7p(ZjAyx8@S;wIE-Rr${zF1vw+tVAvT!_D z1Sb_o$czJzv5-5x!@3Uw*s5?MB3{TwLh+jztD3&HzDFag$2T{u5y_MHfEq$eqPuYL zk&4UQ-OW9~3!aLfZ{T#7&!)P^c3^IVPQGNzoz|E8?&m~wQ)prGGw7+ooj>m*v~Ay3 z?>%6j5pCdiK`fGR56E|4zw1W?>|01v9|hcQx9wJ=b9RGY(yitG^3i?(&plv!-B9dI z-xhpV<%7Dx&OrO?O$a&N1I#?c&a2A<{@epl8R$PVJaV^*G&s*G59om0%%DAdqR=&@ zF|R7ItG>hKBNiXld%$OS&uzj2S~ED@au2|~2Q2yAr3r~$C(Q*+N2fWr4Y6)bE5;TP z)-gF5&5{&#*-{2R)}c{-N&{Ep~)K<93yMH5QmfxMqcFZwKOM z$)J53nj_3A_j*C}F2EaS-yLe;_y$@uAZcV+{&|HF4(1gxl@LjmA@Q7oR^rh zj@g5?ohVz7_RHH}D?edg)+|x61y)X1)w|J*=Cl%xW$6~KH^dq(W3ST@oNq!Tm!arO zF#tFXg@BVsy>4eF2NZD%2O3r5{hN9AD)U%qA0Z*X2DbLz(o?dqw{P^@SCj z;t$J1Tk&p+%D?qe<>aEeaWBrSkL`UwH(C2!5+`*V4)C_Yty<&oz)c*Tf@3S*m$%B} zY8q4Hqhp;GC6L0PxI|(W2#@6F5?q2O?rKkDDRAO2g)<$_mJQ@?^XhhZ-6vt>5-)!_ zrD-@Axi1NP^lTqBJg%ATmm~;v{GvKuPQrU-FmIo}EAcGHo;%^ph>}oleht?ZR^dtvIgwFwJ|wqFM4iz+r(N@zr&acP=1&0gOsw z1YbLxYm}+^-2+O$`rh_lN5^WpyGPfI?CSPTPHkaE1zuPK*Y+Y>xG4u zm)~UPfAE7NTPBjcu37QG-u_jKwTN+r)+7FK(qcN8mz6r8Z7HfLJli5XUxPh1H*#Oh zg;3-ZtoP@Chmy^n zVscTEEY2uq;@ZqlMYYDMk|$iXBoBE&+5lCD<)_}jPRG`u2XwBPL^P1bpy9RE>e4Y66W>j z+1GKQg;`wuZy(AW1S;0|?kfj)p=JcU|LICC=eQ$Wmj)MKuBK#Eektm4#W$Qb7SAV4 z>J{CbeRWA}cEUJE5MEX!sw49IJq*QzYkEB=%i^PFTN&N6Xo#2Ue&GVe7v0n~nd$$= z+PY03!Pw-%!F;HYu^LZ|IJTSIK0ZdKj6_B^Upm;8|><~P9dGL4=`j2Jne@{$m<#BV?#OzT%%+itv6m7K6*=LWf zrVn{lBcCwXWiabj?Z)P-Uk*1c z@r%sJDw%tJd1zXytdf&&b^Hm zD1ReSvj3A;0?r5SwXHrN`xGt}UBw;|9i7QKTRWx|)WWWS@gvzgM6mp&ZDgl`252&~!H(_Z|Vqp4w$_>L-J817(w|l97bgL31h?faJc{R6Z z#W3#Qs0%~C4D|m`PQKafH-qNWd%zlJ89E##@+~H zp=@N~r@ztBR-OYrw*P&!|A%$_zwux%CLlu^Shu_5--HnMW0g0UA|JPXYiv2m^v%kh zj;KauYKotxw+2>%g{HM=c6iFvq7;g`QG5=^$R>p@l1411fbl+YRU~I2^)6VG9(A(O z(c}{_;JfdD#YZlA9R5)la`>>wT-4hl{IQZJsrHYeG6>bmU4zJ9_llQT?|#nkE+XoB zX-=D(fwdSpgL1G_>n|!PvQ(M=gU+{R&as)jD28i`I?wB<>7D(# zhgPb$e1B*UK8T}%q2P#Ey~pc2j6kv+HTY{z+*5u`R zHSg(?k470Xe|1@BTP$K8CT6u}&lNLmG z1<1ef!`$-3&d8Y=wY8s`BYt%gB06_K)x4bM*(Lk}*L`!|2Wqoz8T^n`L7#&ww0~6X ziL@%6O84rI7;%(k-U5=nk3MXxW)W^yC^9xc^gVzqHm%tDc61Qn?ICL_Uk}5%CZ}PCL%TLaZgsv zs!{S)s^n`3_IovZm+MWG_CkZbe3>P3Xq)PTD-2Jk% z*k;9JIWR?RlQf+(vvkyGf~%_`sh*~0FuGu|^87bvYSBKf42xVv&NJFFQPgzfe@-EL z`)x6zQrQvPw~x8sa8Vf)G%-?YK;+*QL|nYo`uvIWYdoUTX(2``>f^$KiNjJcQT%+0 zUWq0O$DP`0y5T1I&UK&ZZih=)|L9|c0;e66#sAN0s~8V;g7*^jGS}5!xi*&1VSoYc zUjmq^kB95 zAia9LB{T4o3+sI z!!WggFQ^Wil9WSxdycoJ94>5;h@LQW`s7TsQ0%I>qOumoW?Kh)^~qiRm`=HHrHJgS zBsERe1EiZ6jFR>5kkntp&A%rW^+mLrAXU-XqrWXCJ(A%%TwwsFGi@uJ8jQAu@SsjD z?zBrj|MKX<9Vwdf7J#X6Td4bBLON{c@gRHw-8B~fvrB!g+XGLszG6u0QG0{%%TI$f z0@@5l`Pt!lyBi?4`YCdbDH-zpMZ3kDZ+BAnfZWWerikonw^qG@(I)iq@QJMcshZo; zMPd1%(OC+`fRGW2PD{|EV(c;3I3vj;m#1h=Qt~9Ard11#9N?lO!_zbJdODg-26m}M)Q>8Wun}MPdDstHNghgi zN^Ymp@lqRh+r5e55w2=t0Dwu*GWD$P0(f!{s1&6{>CY@T=?KN^C4iE{nP)pBELgVx$Xf~ z*q3O|;d?*2n(eN+>~8SgE$8?7ue!c_0C>dm9NoYu7(B@PNobLsl*OfLVL*<2DQa^cK1fg(oCCQh_< zfG_Pqqn!p41ABn*!HG2pxbd5MAd9+@B9mI|+G|)iV<%t!8p{N%@%ZQ4CJ$jztZBYA zQ7REj<+$#d)f2QL079uK#Qax9K;?O?h=?AZAMT0DwDOcVjn?a;yuz^9u`g)QdLptm zk<)!h5iU_g6WmD=lFB}nn9l2_DwN-C}JYPvX9Au6yow` zflp7&Og_j`su$caX>5wmE(%n(vf7n?i*Q8I+X_tt^x&3NtGbnq^_3XCKg|N&LZ(`F zraw9Mj0LCis?cFBx!#UnlA|);+xwpS5Zm!?zWbvb?8Jhqxa`8B33p17;osxE-Z;Gn zAj-7n{n{#1lhV?#5KPYOzwEojm@NRo`p@6dHWf?U85j)7H?=-9vqY92s-hj!_ia*j zw?SK+FW6@Bn|==Hr0v_*+&Lh+H3w)F*k5aOYgCIR(xp$Au_dBRW|y>4yjUDD>`6Su zNZsr;Ic$u2aUg-!2kI9zkK~D;@OA}Qh#pZ)!4*1vd=lF>P~Lsk@Z>$~5!TbJGa}?4Nfnf{u(kW7?Wt?b=O+Qe~aS*-=+5+mUS|rAdDLaTSO-33}Zt{McYnhgY~$T z2|PUzoKsMW_WjrEygs$v%k6ad;iXAxo>Ej*T3nbgmNjwU18-}iIE0Ud_OoV|Z%$=h zY0buz>|&&NI{n5|R*N%c6p{AdL5qEH7RE%*>e`wbQi>%P8@~+q46m`#U9RpmBY#2fSE0$KS zE!ZaPb+nph)pPZAp2Sg#9WB^=0gDlXWDBJoS*JXFk_1Bq{{wZDSl&s`VS96I$&Z~) zVBx0tGTN{2x-yv&6Sz^G_gDT%REfPUh_zI_bKsLxm=$GeekX#0dRqk^W0v*&A1k<#p2HNYl{|!l!GD?SGVmhBagZV8PU_ zNwl~>E3JQ?Xg`@wOWa85~zn#Y4aO}VbnGVciV7izTh8|YS`PaBAx;~80e$DlSSs_U(Nbl z7W(pLU7X&uioRO6q$x<^8c&OEGj|Z&do|hGZr?pIy+2@GXy8icUxZj-Z`Y(^(F=0I6Lfn}1 zc&(wE$dyFq?g*!oz&gV|CIx@nKYu}yXkIn62O-R0;$ZfR=T#|^^>t|9L@AgM2<}%= zkkEOW$XO+2mY<%a5+jKaL6fRkz(I~+zvUxT1@y_%l8?Njso))3y3(9pk#ty5rlox| zj$%OQWUGDxW6xT3cKWEgNikh?eumAQHbb};5$i)yKVC_}=XdCu1P5Z_(gQ{=HdO8L zj3lSB;itJyr4Tu5WO29MCGQZ$(e%uU`;uZEM!_&b_m+le@>m5s{uA7@^Z!7 zp*%0CX7OLx)c@;k|37%cn*L_hpYI;WG=d&@-A{F+S;>2|HM)Ihr?*nF*C98CLD-tF^YTJjtgS84qWO%cmo>hzS@6Lb|V)4-je z(`{x@*Ildx8rkL`DG8f>#VBHxQE2`=$G%y=_-AxGGP`u|X4TqmJJDu|gASWy0iaDRzf*5JONMXFdZI5Cum4%X`Y<{&|0^e2!73~c2%fgo zS0=(_eS}{DU{ffj^O&A zMuv|h;{qk?^iPOhTF%@er33>T&;WA+{!Qdoa^B9M5e3D#KpL4muAi1+UTIO({VHoP z{J~|q!GJCxmTwS|S`I1OTQH~HFz=1foxHS*Rh14};y(M6qU?PA$Z;dP%zK(fn@v*P z(8^GrqwuZ4iN{hfEWwc!{a*NCw+iR{w><~ zczTkP>S5&|<{5m$aK2TkW~r7mPvS76;ee`QXsE7<)%fsP>w?U|-<_c}97N#P7p(X~8skrkqiH27l3gJy?9;PV_ zrSBa_duA|F!}ZW&zqh3}mn;FWAyIDhFq_^3Rs!G#j_69P#NE#}0d$ua<86WVInV7( z!0weZ->nq-nAUm-xe_^PDr<53#p^)wmXIf@ePn}cjWgk*A+n;DyHy*z!HVu=qKRvw zuZ*?`iyj~2_^`Q7L*B>C%KOI{x6E2-cJ!|h{Nxd8Q4!w+MB)oOpN#c7d4repUT=6* z@+%u8Ks+qgoVjt&1Z|Kdxg!64ey*80N!VcntNQwu9#Hm3F+r(4Or5ULj@>c|EJa9O zmN;*}R2H4dLFa$~t=R969O}x#S41lh6VY4vt$*Fth~w+c+eSMd_dfFWPO_o~_b2PX zD+Pk~<%y^hNx?%P%KKW{2ac*n39q*3cD^QLX5xG@{k=mY9UpR#Ev~Oil98W%( znku^Pimu-N=UngqT&=I%HoRW-q~n6L*-?3z1_HM`tY}33C=FzIFP$WG$Uv(v-^0sc zI)srKMfe;@yEy9MroK9zzlUt{-|bLCFA)}CI)RMwYPkAqmB!k1o3Q|G&8hIt?76oK*g$Y`wdNFtDut3=Xd4rFsWvZCl?_xTzUkEyqMT1HHmu+%WId?>uCm+n92I@Cj zzo)-t&;RJCZNlcyHCL^#<~~pMiw#eg3M7_dbm%kCdXsLn-ak<>uCzGYLpb&7(p3(9 zQ6PTLlS5!~laJ>nI?%gS2|kGu660bU4P&NXbOa*NtPLmSN0#IIz5xlh*Et<>Z}D2@ zhA_32GwSs2Y$&Z;GLNV?-tr2XC>zxBwosdy zoNcuG@LX=AsgXz1U`mNL>#OqGtC4EU&uv~aY4-q&LuQ>n->*mtB+-~ z+~7Y?dcY1pg9IJQbM=Vr805m^X~mU^(F_8+h#j#SE)`j-Tw$EI^>H3j!ue#s1>}s! z?CY?AQr`7FTivy!_rWsfUTizsFUy`(Y`ln=LiaK-P-T@av~g6ypgiG)`QTin`bO`h zq=DSgNc0lv?vwtk9?-bj8+ipiDzVoI@$!Y1xp{mKzSO9~hnFT?Fp^%pemKu7Ktb6A zpzR%n&M7vmqi8q!pg=EsRK&g%XtvbcnNo(?iTTP@O7&wY!8Z4sVv`dQy9bNu-xMbO z_+XSpno6o}P#uRT)H^>qK{$JD{P0Ck<%1=Y4+I+5pFdD!dNB_8NF3gDRUA0ocqGOA z7TY=qPq?X7Z`E@PPWAY)IjprsWtf;j85N1bytC~bS3RAO^E%q!lF-r8h%GJY)tDU7 z(lbyMF|#xK3`XmP_0-Bm6cj!UYi(wDrKilsX@xN^TcOSBO`>1Lu*qeiFFAPl<-rEv z_kPHW0lJeu?qO{W<3ZUXy3{ye3M1V)`Qp`Iuk!l~0(Nz5<@QBh8XPltTQ+JyJ)V%; zbZOeKcpviaHVW=FN!=a7IV4L`H5t~JnUx78+3*;UFvI^DX2)P7^zKO ztKa#(LZtDIwQAQ!pV#W>Yw^9>4x^XY{1L0AD5^(@7kpFcPj=4dZ`&3}sWAP*Ez|fQ z49a%)+>X5?HQ3Vh+1|X*H}j3`@_7N4<_xMhqE3X?JaG>_KsJt7FhPq=l(@s~zXCG)X51oLjnA-6k_kiE{iJFMXgPUpc92BpEwdpmE z*GAo<|4c~-BBUI4ejQJ%8d4exCQd9L>P+C@&32;vOrX|HHlEFIMrpA@9nexxziF>o z)x7Ov$){O&?V7@<^?HKkV~`}ZDdb{pg#}D03 z-K;NbM_^(x?Cv=lt)Q}I5hf~CypHdX^%!s&5 zS=4;r8$xIZ*UKEIDs59P)irF`TGJN{WJ@2`u`TbJE`*yd%Dlz*1BjvQcR91z) z5*0xj+B5|2QDDqGoyEZ|yK>^)rQrSd>B38TJAZe|v_@1;h&93oXIo6N_ zGouZzN#6M!gp8w@@o_($+U26A+p_L3I+^=u@-}`-!rHCk zZ@&PaYMxIvtm*A(@=U`%Vbs%DWt<~CE@B0$RK5nN%G~s0eMc#GtBmq{W|_#Usk&{P zrKM#a61kj*{E?lN?Z=^`prDnNivY&KhZ+5@MvPiv|mX z?uh~p5AsG}dFJ_ONBItr>%e_#*% z=f)NVc4ut!x}orZ8=u+xnRH)_ z(|C<_$CP>Iq6+igcypU)36k%xhVp;UIP({mqfNw$F3mw!V^ z$Kmq3YUQYwD+%`N<$2MnuFNwtN|!ruD@2^5vBxfzjg_9iH`0DP5KVQ$FUU-6ZM*7) zL0(#g73Gg8Q~IkNW2T$Kntt#N14E;{CM4}BXfHEI3)YnW@|9nDvnyK5Z@?%zT7HTa zX9=$>aSPW3U8d=q_fn+DCWqNwqJN!~`NeiZM~7J?xnJ?Agn`wwbV5Wp1h5olX$)u0 zYM}jrLzwxhY$@W~NxM45qBzu>%aaHtM?a3nI&m}oP%kYZ>=ZJRIq}~}5R@?2R1PoX zhi!;K&&Oen#%7XF!j>xbP9Yo=>SJ?mcORn3USE{R>6;i4CidU_#6MhnTn-f@dTF&u zb2!@ljiGDfbFb=!xVJAU0$2iy(rEQ=$Ez7Jp>hp*(s%ahhW1Bw&)~e_zbK3nSMdUN zn#j|M8qa*Yje?vv!kz_FP1=>Do|*atdp(I5{_^+>E-{9e1IpLg+H#36^ta)P{slGa z9|78JHx-1EYRB=yG7UJrXA;- zM#zjlnrs$Zc}AhnY~KQ*AC8v)H6}GvZm5NTp? zTvU~WU#zssS6iylneeZ7Ltkk*S*N9o?=o#Jl;V$ZPVpg#JnlWNZ3Tjtudo5$WOz_ceM#wL;e6R7*KBHk3njCR z^P6S$x2nhCabuhJPW8Gq>ulFYaDi5(&o`)%2O3iM>5C66QYg<>8@!#-aS76`N_s!~Y$s3{EHKd4HPjme1gSbNOW`cWr)5K1xHmB_b2elze?4kULR=WnLD>Q(*4W?<0#Gfak?-J}#s+P7a=vN-ScIp> z*Ru=rVTBH6yuVl$4y(P5Gh!fUY%<}zxg8i%^a{T{0kR82In&Rtls~F~r3phI4p)3k_Sqg(AYG=ON9c%>nl!PO%$9ouY8F`&kyC5iNEuRk2rc|HkmYn4Pw~fN; zF4vV6pWJ9_<&A|e9yz(Ac;@yb+E21&6yiwlFFY*l++E;!U>Uw2La3#PtsG?}T) zHB6}hBl?rT>W7;TskdCOL!Cc0I0g~!K>aAVC{-klrauPtR6WXoH5{_j|Y zzkxZf^-mLj@&CTkGt&w)0D{ zff5gilQV_j{%V24NI}}oTCxYbPV))M`zf~-*>LgeS$aL15Qm5Srbf44PwjVPVJTR% zF!xsxzWfZ)S#9?XdqDsUl$!V0^Muyeq5Qs$tHiC~tlT!E9W}M;NvL=@vtRTFzYkYd zES6UcL%Hk+2L`|81*TtCCxAf)>%qU+)c*9{ONCjhjj8m^hYMmvsUh5rIn(jT7+=!`ZRdre+_tZ*SyktsjUGA*-_S!^Hh30{styxB{ zzp@?artM`b%@uEv$Z zZik2ThS1MN*lg^_Ga>04wWW8|Hdfc;4anqUw2R$oau{2LUS8I`sSzo6ojHy~E#*C{ z@7nXXgQL=3bw>-kj%Y`veTaoo9VyyEb&dna{A68YzDanO%c<@7uSz`|tnYp#i&Vnm#I?PH-g;JG; zdLW`@jLUFzAUj*xm}f2WBR`kG{y2$8ZY+w(ktytI@ui)%stC0;&&DHHvV{#mF~nUd zv44LZf^V)b$*C7{QaS+SKI`;d82qlT4SbnzEbTl1b{N0EHW7eUC=5I6^dURc*ujVr z8sqR&lhn=bixxtM6JJ#~HufOmn*1>-?0IZPsdd&W{VgodH^~J`883Pmf=*wO@zg7? z^)KM`f6pQ$>22MX6!yg#%ta&0D75nza3+))+1A%zELz9MZ={{n_Ie4{88SPF^Cc+jJLB`RKDaH z9GC9>ir~lXuU~~$YXfA^4*+2c)7r;H^%gIasfzMN7G56d5__$Vcv~oypkK1hLe%UP z@_@(e%u(4|cN(vs9BdfyyyHd#zal;AGf;Ve#R_ASt%zf!M&1mXYzAhj&RyevrMhWb zqZH+*s}%ImkX(yTh;F+Ntv@6pan5(v>vPksQ2imN1+HP@E0m*y$?rzRn1klQl!jH! z7&EzmDs;j|pPEls^yY02s#=7Z5M2>yD4&d{3RtXtS$SpVuo1a=b)cI<8Xiu_nZ?f0 z*$KV!Bf@3P+YvZ||1zRGM^1acEvap^p2t-gZ=TnbREEig>1G0A?gcR=_NEKIINWt6tUj0P>>t1^#WpTdjhVn zp#5POPGsO=VqcS}JefulY0UcJS&sh-o6hVXYkNu!tP;a2(osbw;RrJ(@Z>G$mHd*r zX{f0qgzO_n$3u>MiVr#As>FHe1%~v4c@YNnN9Lnwt78`<16n{%U-fk>nv%v9i}8-( z`8t)T{U*0RwW?=?NN-yRbA4%M3P@jz!#NaQ6&$G4TwoHuhkQY&XLHlD zBn?=UKhvDw7Di_o{xv)wmvOZBz52EORQJfbcAYhEwcCego^U3 zQpIK`OOUR2R`5o5ZpbhYYeYKkZNl$j&utfz6F2AdzH2c%ypSVZ-VI2M^a$)$w#vO> z+*$v|d|Kaj@FcNQsPfRUa=`6gwB<7vCg;Fxj-~eYhY2$F&bL}qEcdq3u};>%#jk(O zZOiQ2om_9-=<}1fiz_G#Mj=*T6AiQv6LAe|`&0|kI(~|>oMmGYez|LKgY^u=Au%4Y*>}9OTk z!=Ki*zw}_Zl%E7=+4N%)J%g5;Q;Qwo%L2MMmmXz- zt{WiG4U-bE*j{ZL4PT2?)aL=fyo}*ev<XKsj+BXE1Q!Un zjwi;q-SG*6T5~PMDU86L3deCM$^EUg4O;M+q^z{OTbxPi zrfZSaH*nR&~paZg^F2l?9d8$fe;3(;l z7K3)pd539WL@f^9*1h4LVDIeXz995XRiVM-WX*e6V?N9860Aq%5p@HXZ(I}Sb;11- z?Pq%6b~fu_0LJj9!>s-W<^5$$paUq4g#dG>#`R{ezIC!0^+uP{lVGfLxtJX))owFK z&j44(P8VRof@k@>nt!IsdOX2~-126^EVE@H#)b|o;M-~Fu&x_q)?UcIxu&E_$iD0- zzuI%2*zyg@)ah|gN*%jn>P5aaq_~vy}Loz!Y^6@1-Y*Id3Y*3k|yPs=Gogh77Cjnf4|77gY)^O%1-u zK)0^9FPn9?RF-%Mx3%c(mll8@Nj?bko0cz&ioA+kEnFBW7fqfG=+ukz?SvLC?4TPf z<42nYDxlVmQ0S-iSEPhw&kOQ~JzlkHLRE}Q_EDSZ9bAFFk-U9X93amevfO6^H^Hg` z0z==sl$Dja?18DwpUWTuMNGl>jY9c5xWb#%)oyr`NM`N6a?yr()#g@l_8D9inH6pI( zqf+!7Bg&$+W8Vn>^v4@`gQd>3wF#?B#A4b&0EVawom+iegKbW2SD7sy3$^8-(2J8#9hm{N7Js)iW);(SY&RPyF#zOa)R2-63$vy z3QcrNvhYl_c4w4pm>?J0+rtYYl{yo63g9in*F0U&MKJrVsgWrP%KENEY)VL@u#Bg7 zFN9Yw9;=%x5%no>Miz;Ru49ivIhlmxITP0x zcMGN4VmG)dSq(dbn}P-E?D*A^`&FJ*UFUl*$1wGc-i0Ttukd?n7?}5r6}RXuBc}Rt zOoD1TJIV#bqOrf8o98=u!eYgfo}a@dF~I@|O6Waz?k$b%r~29A&Z6+J^$DHe&l=rh z^(xfCb4{b7mw6%g~srrU^j!}#wiS7K@?p{(MmyL=M2ba{OXgA+*+(p9j~YH? zr$0XFBp;T8%*QXSiNj_72dPlM|m#3fB%WM z@h)M|n&F-PiC%yVL6+f|!zXfb6)-lNj;ke?57OFb#yZj;7&7z1-q zk!E`5VPS=0lHD&Ox&*6c*alg}u8vE@qxbBmQm%f&UO*8 z2ZuuOR618P3!)H3-GJUwDUSTce5xP)WR{4LKumvh4UK29z^TaO?GLpftTOV^t-}NJ zeB&fKo~tA$RDr1Fd<)d9{EF2IN4f_CG6(4=0~8Y`&W4fUPwh=yg=?seR3-jcrrEP(&HM#;nr9SPTfTFhq!rX*-RgHZ6te?gfCgwgl0!o zqiWNbmSu0sP-+pW+`lN`hX;sAod)F%G@CR)nsboKD>#-$$$?5O^>zL!}@#gCzFq7R?^ zq_$%rKZ2;0PUm{6w-tb$h93^>?!m^=<8e`x6O%y)pb7WAt#y`XOlGT2X0}FCPus%2v02>Cpi6LW+Q%~Ow^}x zx~83AKiEWG4evcH z#od+MJY;)bG%bBFsjZ`I*K@uhBey2|c5Nv`Mn*HbFuP4jwM!EiG69j1d3iA*2~mVn z^GgDd;?HGZ26g&q@4S_UhNu+T6873ElxJ7La<{GgXbQZ{6(st+*4W(@fo(I)iX#3Y zS5DQ479XUE_H@f2``fu3`Apw;I+uV;C=kKSPmzY`s}?@C9!*}fh~5?N!d8SSS6Z*m ze|H}E;pPt#Fjo3p)0a`v9Q7$`Sj*!;4&KE&?{{W&956!N#jbDRs<@FKQvka);Q&!c zR`u+A`67{Hh0>4v?!;uI@owoPb(_=|M<~zN^aCXe&uKdh?bF|?=wD}{U-Woqmqnb% zOKGEbEC;3IJqY&oE*o8y+wEwqvW8#8sNZ^_Ek0u(r*6(7_9iYVK3Mmr?Pe#(v$f~n zUD6*?lYPRT22l7mtEeyH9xiu(pV?946Hcn*{nRrmBO@(OtuWW6X<(odcug2N9kYQ? zUR(CxvCafIP*gtRE)cny(A$XS3Fjwg_1*$TPs*qf71>?T_1U6W)7QOA=zOHosEPc= zXj`~t2a+|HAbr{A&z_aE#CAGQm0t6Xe}O1R>IH5$UDwI`ok3!T6++nQIvDHA$XIX} zgBO1KTgFT613&Q4#d&jPfLb?v9nO*31~T1Snrl$h$$&`K;MC1uYqqWHA4Q5o7j~U~6a- zZYR5)R)rF#3xFIcww?LF*?$sjkFBPiPi&z*V|>@TmPQ7A{+4(D@dEyLe0K2D^}~DC zhI`Zr4hw9LU~s!R&NA)p5fuan7nJo@Xij_;QxrNsC7Q}gghNhWp4pM&mb@4l;MHsS zhX6j2ZtXas!Rino;&#{q#58JYjyY@FEo=sI+Jow`5&mrJ%3Ue*XiGw&$H5Sp%lt%a z9QtO5Cxyg)hz|7lcks6KvB%+bF9<90gZeaa2xHZdn(XZwuNbV5M0M3fChy6OGRb)a zes{COQrqOzH2#UDk#jUc>s@5Ql-aezgCCqDa}swOu6aR^{Qdm`kghEBxQz52Gf`g0 z9C2XMSE+yyv4B2BrY3gHKF;hugl6hWb9(-fm3>9l3e$3s%W^+;w`Om-l{72JyBt_C}oA5l7l*bFRs^`!yKWcXMcRvEXkojWhn8I zfTIX{9;<}T)mhr0#WtLTc+cQ`zIU4o@d6v^M`6nXTj<#26rpff;!G0zn?%Cwz?mON zL<-eYWAhItCgxI$SB_outIYA*jore8drn0hk1!vV3}HC5!;o~b(1IwlKwS)#mw^2t z-56p&u6f@2wXE)|+VIDfGGP(HR_1XZ^d!s`2@R_9L4S|HT3t9hfdQ@hdi*Qfsdrfg zQ9@|Bjn`(`DSQR~Ul*NUt#b z@OvaVH&XkPK&&xxXemGc`b@$iIJymS>$RcXosri z+v$o5vTTB#?3r`L(L;yla~nG`xqJ+~{wvT77oB^b6DBczUIJ$oQE5WWx| z`^5&$s1@4tV8x2P=n=4pY$b3NbYnsMMkMF`%D@Z`I2+@_WmK;Pr+?LJm&x$}57zI=p54nD7)-vCzoPEC$ZpapCM8ne9M!LxluvXrx*2K_YY#dOPGJlxxW>+i--E<}su%QS ze0h2dZ|vxRzQuZ^jn?`yrU`^HCXx6#eMgM6PJQ=@24LA+fI!@MH&-gHWLn|m&!_k+ zaR18-jsKBJ&oB=h)Z3|-vYiBqBR?$I=p7hOHqucyLO-OoO~QXE%ztCcdznYqYHscg zY%S%=B|_^~(fncE0G)P#KU4LKKO-bAV3UhDosfrV+HJnhp}eP1@UY3U>d-S)UveA# z0mEMq|DgPi(T#{It$v6)jjVwrujPr;U(Y~N*M$^X@^S_Ydz=^74RDvo>SXyVIacrT zF^`S!k{4A^4s-C4SQl#xeke^nyZS>43R(xK_c#L)@8i-cVqH7$nbG9Zh;`u?EtALC ztm?eCW;!+_*p8vu$M&t8QRPuArcBGWOe$lTG%FG7!VP%5Tl>te-ORLx-K#K3wk~m6#|B=hY4y|2yL`l`qtw($v^;PQR<#qZW3W1rcS~pSz;`ky7_?A2#P9+%M zXGQVtE?52}U``8bs}XaX{?<>uv%^_c+(yPQWjVzubt+5p+?bwKHhQV+_+q-LTWq zPI5L%w~kF})lXL*9 zcT)N#T5>1{)>Pmd2A#Jcsef05{BMEEUprWSOpgL|#K?50 zZPJd;4kPe1M`Y&{12C^`knre07hvG_*9!d#bA{G$z?ZRmbyAS--b`gvr}yWY)$7o` zO;jiQ#*=R#S**=@06$ANXLM4c<8a4{>sz*G57~2GxC-vA7I@WWZTU;wtkUeEJH?!< ze~3rlsX#K}cMkQpa^EvkYnmtxNm6)&pC;0!zFO3l@onlO)CW)s$2Divbgin%AN0#$z4`Ts%>|+ zCY~_vBjzWZ?FpkUJS%KvdZlfa!-)K5Wq6=LXIuzv5W*cE9W`S`@720qejR1z>Z4B* zJozS7&O%sxDrJPWse=C+<@uxr{QiI>>*Y){?JQ0%huT^h8Ig2>wPl&1l(Y)b{DMkW zn|05=hKUsaN9^B9rCx*wfzpC9A0cMiaCpxu5LuK-zNc7f)7n_EOTi_p@BNT7A;kU2GBR#~ zZtlDY=a|6NuIBv7!)cA*HpgEP>0d_GU)v|Wtx`l63Bycg3?D{g*O zHl5ANi+|`lI%|#$LT%S*%{k5GXGfFdMX)nQl9d$(e8g>PHh{Yls$e0aGe@(oQqKej zzEFvrWclTG(*=42#2vdOyW%E}6HF@`iUrpbDZWOtBd=1((kZ^z^0+ljq>+96kSdBv!&`3$OU^?Zz`eZ%IU8)voRmP-0wQnjdjDYqJG)BHkh7e2KH zFP1Cx7&Hn#C%V3U>(cZ}lE{6%>KU=iGb^8@m+WfbJkZ-TKEPvfh1A7f28DnhH&CM$^KclU^Pz0=qrrx zZuwn#3fSAZy98is(SO2O&K~giT)UqH>02oVRBn^Q2Ie6#r4Grg_KI7Gc-s1Ldv%vZ zv(gIrK^7jK5T^?5YNJBMqAVSBb=Q=>0lp0OrWdAzK(-`<@9#aDUpW8T~kxx zOE*2}j&DCRRQKh0A*+|M8Y`UorF-V|o{Pp$Ux<3iHZR@=DdhDE6gQx_;jak)X75tp2*=Lr%lfukj zBGaEUe-I$hq%-KskTCI5-{2nQa$LwW8vj@-8;ur&OvlbCA-;a;(Hh8m^KCJaOuQ}x zMzh;k{0Y}sD_^Zsgn6?Fc#>E`mcQ1|pQSa^z)5kqM%U0EQgb8L=T0sA2geNV zdA6B;tu|Vt-{XIEXwXKf;5r)2C%ug6l~0o@;=^ zWP->h0UO!=8+wEo68+8a#eHX(ZTW=c3)2yNVe_H=dBt)oBePu8^AH*bRJKFryXVg@ zDBmK#tge2Sz@E4!N?lp~*0X19fLz+1z~e%HI{BBGQC`=zf89d-a}WI0Q~cw?f8-lA zk`Ld`=dP9jF>Bve%fTJB(5n$B8ND#u7hxtj+(qTR6{-xx) z+~g->eSHx0!A2_jH!MoW9S-UXmo31*GK6(qjo7w@o`1PoI?zN-;_1^mF0g;!W>+Nr zk;+ekdzjMM)sY1qRPN?{3CFsA=#c6l>QSEp{XFNcOeLAm=5z$*WG=QtdXpJvIrkI1WpNA2@uAPac=fYPh?r45y_oq5tNX~eAm`D>^ba|o?Lgi-(>pzpuc|2#~XRkmo#b3 ziEMsr=kq>svu##l_gq7lm)pC!O1B4zH#H;!dQ>Dyn_EPES>)IcLj?{Rh|453qw-V; zZ-Yt~3wKvq?ynA=5B_(V39k7hLj)&_`?BzW;z{?EP~EMlT)RPK$>^N;@X|Z>GC8@4 z3Fi^NZq}C8zpPZxt+A@nU{UgQLkspa!Q50mD$BIPHgp(zyIR18@BxJyxBWAwiKtHi z&3p`)1G?D-9Xg`kX_OGiRG6e|rFDB<+to#0ZyMXaP9?1*&%KCh*;w@qiI&%XD`8;s zD2whBC1KXiX8nI7G=p<^a)7&GnlJ)CDo7RvUPZ3csi(xd^Lpj#eG&LqSBbg3W5`c} z4{d6In0wS4HUw-J1M$k3zwZ|RZ;hx{0$iZL*w=~E823$%63ME=RARmwr;6^J_=p>R zZ9}GyT})-U*q4!d?ew}puC}%$I)$yNhOO0za@GjHiWagiaq}^Ef)KWwy|!hsRlTlw zJ5}m}fY$Zr_R6Xe-^iB%BE#Ef#9+FrJ9setefHXGvqIC;ls4}E#XgcGbRCm|w`CEC zWXqoGT_a;Mktk6W*~-;$$V)a8WY?<%Q+4riCsp9hlGEzi)&3U`@n3(@FROENyKsXk zYy~J{T_V9RHRaW>yO39?Rop699aL-L_^jF_rl{WkRzpSd55n#tY`QK;re% zhL37B5aV%zJ9hdt#O=n^_uM=lKS{c85_fA!uTB&9B;Lu}@%z)7lQt*2YLkC+)SzA8n%8tInW_d#V*vfOPwE`VQB;R0XqYw?Y?mnt}tsn>0X0V`0+ zNYB#kwKrt82hcyVR{6#>yH=aZ{>=^aF8qch*Qspf_EAsnXU_E8sH_%Ox}H{0|q|t)4ULO{xN{%MGAWt@tH?`rQU? zp)^M!d_qaB=OHi{>b5o$BEez9Nmt3}gN7Vx1;zR{^)Hcq3S=_HH6CuUIpFaJeLu!Xj_t_ayvN zq37NDHT3U+_8$ckzlF8mV)H%yxudY1hS@b}zWIVEb=TXk@{9)LUZ0gN#U5KBh#rYNge4!d0bfyUE3-u{DKH8_P9UkLY#f^} zrFMmieCNA{o!AGe$L|8FbLvF=Cf*vF=<+QiOEGnVQq*(Xg+>jlWW?vA}9NB2=dUfneOo&Ie%!G##&TKu#1mB7d*I#$>Q zP0A%l0gI^P^hs~;gu>MO*lQ~~AF&~~iYBB&xEc-{7IiJ}35kNg94peuxR!Voi`b%iW5-L>64g1E7PG#2-1K5eIahX7gRoR>~;PO{(>=-faS z$1l8hMPrCZsm@Sm;WU2B6T;ph>3U2j*;uS7C^Bl zaOXkBE=Aq}=uQ)kZJxprT7!0XKcHc+lm_N=5C%u^BLa?Or4|88`*--WJDA;pA#nWG z;t(lTPV|ZA=vv70OJBiEmliI*xnwP|^ceh?CmTyL$u041HjW zea|jc9&gD(mSTN4NPA_yVX0wIyBs=7{cV8vB90AFk!RI%$(gA$_LMWV=unU?FpR0Q zGq{lJEn&MFfr-Oy+52vcOA4sTZGyQeffyAza=O&C%G`oH0^bXQ;th?Z{)eQsa+zTZ zQH){CDRGO=GMb6~cbP4<2nHUvzLgYy@Qhw^qas|ul2(b>Sc8Eint&i?=@~&^Bq3VK zWUwH0>)-V<|0j5@@jtSh8VQl;IW6fk_B!8e^~+Y9JrqbbDB?4GT%Aj;X*0*vr>f`v z8P?p^mtw2_$4@{%l*Z-?r9dM-)}OJzk>nY*L*Trn}-?Zss+K;9qhJZ&J;kh5N2 zHHm#w3^OnvB1x$jgS`ZF2{#J$wE)rZ5hwVDex#W8b(_)(&?^(p7Uerx1f9{s+(1qfQHOC1+R;8fcn3jZPtH;9kY`^l1<0F(-O|zmkV)nt)3dU&{HT6uB#m=( z06q|)kxWVgG?IQ-82{2p0%8L5e`GT2Jb(*90hsMYn)vBqFqLZnZQY)`PkU>7N4ok? z0=Up2uS9a*GYV;rP1L7%n)m9SL+#*p8L04NC~JN*a0X9YUz#_F;OI4n)X!TWdai{J zXS3gaqGWsI9aP1hm5q>VTh56QF5OjSXWU>eb$mvk^2f(#6<_~%_M-ofk(>OgWAxTZ zYVFa5T~=0UrZ&B5p($#^XLs7gVzK_U+q%-i)MD@qQf$}AoxJzfmcqlvCKPW*A_zyq z&y`D1q7vn`qlN6u@f=0X)2E`wsxL2IeHfnQy2<5wk@A655uI8L14#&V)V}!ilS_bM zwOoNBC#_$LvHizjIftW9;^Gk$A?dk#0{dF)-U80CYLV>B1prYE#nwoY^=m*t^FQ)N z{)rr6{QxMb>DM?+;bG@pFTsc+wW$=-)Ph^+&B135b@`Tq0$;nuZV#D#WUm&Nk|}Tz zrJNN3Y6Mw{sG@V^goM~5YOAOFs)q@>t`EDBZ{t!}pA@KpA_&>e`TDNeNNGs7ORbU> z_*Wfy4(zPC@Y?Jz{rY%*H=op#7_C1T!Q>FRSf?te8x&o1B}e-`sS1gQMhstQv<#hsLiHjd)_(a*)aEzBwn7)T zM!LqdJ{Q}|8DG6Z*6Qmq9|QfQg|;TZq%G5uHj4_iXUyz`N19v$*Z5~y0$+cn3 z_!fpM=+MS<@IC$`k6lll>n}RjPr!xsYu&eGMpF{VVZ1Iqno6CK-)EC5Gxh3gtW6@v zq=V6g2_bP8rW<3y?@cUVy;n)n!r@mK&4ezE{knn0nTh1t{IWWUlGBIP3KjM5 z8{Iw}6=-)RzSmd>28SO!`%HWu-3j;?7O09 z!2MN23PKsSMr^e77*=*om4PECaN?NbN?cCmt1x@@bn*0^P)uc*nEP{ zFZrAV>aig=C4cz+VV%dicjpM;jNirU9-?*ozgLj*<_&dNh~P?&0Junk<@SE-4Udl7Pn5>&eJ? zvvBZ>=M_`;SeeGvKX{=}LFngQ#n9tfnu`8O@TZtcdU`QySHLU!IAPqQTVHzXP*bnB z9$Z5A^sa)uJ%$HWdc9IHT5hZb!~=6vo3}QMb=)ueMn=MQLc+FEYUNoCIf6{LJqVhh z&D{+FMfB^{SrcoxoImf+i*lX1ZS}h0utSwk!z`z3lOiKqfayq?TiekPfj9xo$IfIhbv2k%AGS_dG zl$~UUrzu1;KG6Fwk}*KvZ@*TWVC$8)u2_viKI(V1Q_)oGmz=*~h2v@Io!$L-)GZVH zGz4n0cbkYR(K+&RV&J4d#adTeVI%18s`~%O;UBqmyzrA?{PHH*nLHMN>guTa+#n%^ z!lV^ec!RuXuc;$MDPwIoZY_Xh^*(Wkqf!U8?-$?x_P4R;(gdI@qj;(87G((Mi?w}c zBgBuW&5i9;)Mr`U*}u1&EaC~`5OZYgpb73^hGz8lH)+UfTm~jP2a4SSpl4LkF(1&O zr#lOXZv+KR9Oyl0SfjEE66RIQA!WT~pv{BeHy09=5y0pdnj}uhr-pO6nCw#5OXBS3 zT{6=Tq#7CEQGH##h|@{5c1qPQv9+p4PB=J2sm=-3CV!*Rv&sa@ku9H_pqZI=FRJU6 zjkmwntrCR5+zD8<%<)`9# zYe|RpIqV5qBWF9{xZZ84l_r(i;t;ATjK$JKH_K~Q2@^dZ-SW-}7k>iQXvRL3*Fh%G zUStosWxzj|a;N#4@;$+88qeGv*4_jH98OilX+%*Su1KiQ`aokY8%xdUs!Z}3`XXtk z=TcrP$E1cz_)9fo{wJt%3l5~&HLg1kH0Y(Y8A9nk&3#Y%TlV)KMB0DK(*L&?{%w1T z=T*nSuDUL~U+JSUo$SvkY)2?lvcl4|Wpg_Z1!A!tAt>K)Fqqb7IozU+>9x_1kkitI zKU!W_ZM%tHxjdegGw@@>C*fW2XZ;UQk&kEzO}J zj5#AaKb6dZ_*0p%jg3|cVwAu12tE}!!Oz`}L(jtK?=?I)Y%^9e5pQ;1;FFl7t8t&# zT9ath9e7%z)wz1Z26doo+SETVivNf&L1!l? z!&T0P!Y%V-zAV;m{uwu_?#bY!05GyEP9V65HF zs$rbxch2UI2J7_gF0;s*&K~N&&=3mdvNIVRK3MQNxMyl`nMG*X@?-MK&MHA9ag~-j zMEyQf;ZK6|+?C$H_A?~nNJ-*6-as3e-%@HbeSVVCx3P@Bb9nG#?)wT*HUfZUMBjo0 zo|+b*%hu@~Y^m7OdU%~oKrng+e|?%5r@Q;`?VJl_cqWdm^KoPEai0gjGVTu;R=fl# z>nA~gI3ZwkU!FQGMg~6EFa2e~_Gj9J`i6H9Wwc#8Yd9?)kXbC6xV`SZ^(x-yL6e== z#YNNF;=#jH*UC~wX~N8rdqb6wKo`W!$j3A~(Tn1FTfH&s$MbwD2U|8Si+e>676GbJ zCC-eMJIc?Pkhuq}Dh)98^2Dph6PCRjWF%>YfWRLfxWd64RSN`GG)-VrfBzR#XHym+ zur|@$+7MPe==#6s?*K21gkgH9{FG_VO;c9DPB#IoI||oOEsEjh=9&CvEOhwT5?VVT zvhs^2Z)_&8<*-6G8PR>9vAbF>5v|{w2cRL{*NVy>Wk-@WpDL3-m+h~$ClHtP(pyf8)m603z@~WkWA`(Y z+#Q}GEk|NpZG&=E@wyc_8MX+%_ego01*3$fr%GjQT&9uhx1Z3JKLc69&RXCJBkM=p zV`CFmUUAUf73{t>N@UGzXi)Zs=SwML&-08rmFOqSNZgBulyffeEa7P^ufDpg2M5e++Co7lAPimTdg~%R}NjnVazjxF>J?8&67yfOB2Q|$zJdCBX@7sCM zsB@eTRd3%$w{{VN^a9{{z)qq;5)UVZ+FO8B#VvDnE@wzGHpADa1D$vrNQs$={~aPynGn2TBu zQUOBph{0F(Z3bE&;8kxuZpts2{+^fDOQTAV(=P`Oz`p8Bg+x$02Wk&7Jj`h z-$PZBi;ZI^1m}!#71LK_7MOGE@08CVbYI~8BHQnlbmyY)^94T&<(?QOD&!f$SHlC;+ z8Ijc^vwj(7H~46M%JCuN4DB&mWzO!H*(LXQ!9oCoOUhn~q0S)J>G) z*x78PfHni=Tkx%{n-aB}=ZUZT9G&jnKvcC_m6~7uG`kMiqmA0{ZnqFmxyE_ed87C% z)vB?C$Kk>Kg{=(;&3-_x=VyQ|^=t`(Us%ihN<{}n06l3-_O=mH^!1IwZoK4@KS6|y z9-jbNpxwE(2!KnL=y$kO-ni6s<{M>S$t@m`Yr*@!Qnh3r0W7_VmIs^~@OQ?6nu87B z7ixgI3h>X&6Cm5sU`zP19Z4J4=)npvcfEJkIa4g^lY*O!C3zm=A_lBy!VL_y6^|d4 zy{Sl=3YwGr)G-NiYuJvkaPLqkn_|E4Xu%iyG3P|86UNk!SMPay3wxc)z@5Z|nC!}E z4O{nM1aQRQhlZRyh!Ay(3`vln?*(3QugB|cT3Wpsc(ZAH3sZ!6P(J4T>?I1)lit=M5Smcg3uNUJxoPr_?acHp%9o zAXAf)I`i(-ydTJ#Vyv3;v87_0d=P9FG_}+!OFlHLpdCRRR`?*-z^bDhw!#pHRX2!! zlxWnLJ2qIaNSk1kdvbAlP=)N5mQ>J~%fKWucDxdX5PZ|F#&Cp?+>1{5bxrM$=KTBJ zM)n?hn7f26c|1875z{2iOGMepf@Bip%w!a>Y|8BXoNdlmm4a4z0%Dg0Fid-b#8#)LkjL{K{neNhpvGCT( zN4njbJk}*uwv6SU3nV!|6+&AlpDfuP2LhWxKML;Vu?N4(f`7ANXhnehr-0|&2wY=#_B*71 zqEvIM_vkf0+SofNT2NFuqNqF();(MK@ZvHd8N_2%)B3Q3y}kLD}X*PjSZ#|CC%wzpk9PQ}ztHbme=12IA!BjKlBb$b&+!(*LvK=d-K z=iwPt`p_TZZc`muey@FGxONlAxt6dq9U>DXI!cq2BbGiB z_a^T{$(y@G?i?n8P<=w!QAO(UxL5{FAKZnt$fcN#_%95(SK-+U?MrELZNs~_drJYc zik*n(Q+irxAZYp{`2zmtaiM6F+x$lar2sVD0%UCX2h;E$u4R0S!qJ)#cyCKxcl=mN zQ__Sah$mg4k_S&N=~6e+P_5Z#V1=sRlW@-B8a%1sZzW7RpY@cPOOY#%k{HM5D;nve z!V%1vIUlwTh5VH9B{2}5Q(}?0)E_-zj#ARPxOpQo9Pa;>>WN1Ozs$pR@bJ=J+HRWB ztQt=yNJszTgF!JF*UqA#y*@Dop&EFLgC~0wV^r^Zkrc}GX~~$}PHT)*T1Qa$4S}e4 zdU$=MIJ}{5`*m^qbM|5=V3J=KjQc1JHK67hOa`bBf6OR;UC%`N9JLDfe>lgYHjCO;y#Dzf<8%+Q{~S`eNLON;7GdMo5paqEQ~l_=JX;JigD*~sWN-4M zPPLipfzQN$G`?dy5H{}RthU&9O@>1bK&fwMcHwu+{gPXuvmG0fCYwTC*~Ney#!rY7 zQ&XTMP&+6cJ_)AfLANaJ{ZLJo5DK~<0Ihx|r|GN0XYmsI{NySs@=0VoL4)z>Fwnn? z(uz}^d%L&Hf!JkijQ3vYs0-j|CPk(SKfXvyP4govVhU%=8q;jEDn`R0(5`5F+}aKf zh9M2F&-%&x9DU|9FOCivrT$)`{Jz#G=G`}24x;G8quXcC!LGG$iQz1Zx8`pp>KMmQ zvJz#()o+^W&}mebrOM#v119FOy0=A`k1ji>hgfv{EqFVAD& zT5@InGW8m2q_PRBV5Q$>m%$%|X*(@I{X&GoUNGYHK)zOilqzJp&~lAlEy3ANls0 z@5#Yx53d$kWC7ysav|3tYodBb@R$hk(Z~rKaEF$@HCXb1&(d%00<@yI?;o zSn*=pfr0DNrPC(|+`;9+bYO3wsU5%=`ykr@69Jj7F&7mUv2B&?PkDHg7 zyU(Su!(_z?e8wvC<5Do^uVUL^Tj( zOpL~74<0g?1MzES2ZSOa19W=u#gV4--pi~3GXyh#HAB!_xhXaCAl9&hlcJ(OvtRLc zzk;HK=AcH?Rclc~0#(0K(Faipg^f|R^6eci4j0r!$5s#%au{LI^Y#B3;-E0#0lWGW~S%@E>6;aQ4# zmVq&>*uEp~-w^RCtpZnk3v`Vl)jj((^3GL0il!h7Fni2+Krs5WeC?}Z1QPz>iqEO$ z2+g?r(IabwLl^esj~5fBG$vv{`Ss$Fd&97B8R-)a3&gsPb_oTW^e$h&G&Gp$78yA(m!LRaXUm~MO3=07)akb z4Ar>{LwW8F@o2X&sh$uQ`ysaZ6pzkj6=XJ1?-&wjcDojOXo{gcoQR{mST4R6R$tS2 zwzf(epJiLh{cK^7}ubUsjUM=t1VnqJ;T<57f`> zBpuH6o}K7e@^bR)pvW;PtiX2iofgZJU-EJb{ZU>GdWnhrDAECa@1@aH@BM)&5G=f+ z&e7^J)f63_TmXvJ5Un4eRa=9{9}>nuZ6mDb-mZWm(j!D8lkEH-ptx7tHDE3Cm!Nko z3K=rmz`d%*Om$)jgLNoAuCP|H_NF4VW_;%<(AWqWV()Wbw0v)t_c-f=OS$Ga47 zR==ySCt+_)a?d3e>YT;Q7AEnrbH*ACpW3;gyBKF5$uoCOOiqd}KpejL3ppiyZOqkx z`a)dyg2-cv#05rWeORac8X;kH$>Ih``Sj56oQU`)Oi4{ySxt*wO|YU4&&KC)>FBrk zET4DXX?I0!{aB6ggeZEAska}bEUyypVsJbuL$ZfdETH*nMvFF7nqC;OI1qm({sv4& zNANJb7{(KPK1?>iEjqvmP`7Bk9HW1~Yp5|PZRTBC79!u@j&&$bm_EVrVkYX+xLW@ynt^heF2sLvUYS)uW3f$-%8rQhnjyM$oZ?$=|B03ORSAwq?itdPGb$2N{KIQ8Ap$wVzD)llaiv3k^G1GaG&LF z0_C9sONfww$wz4+x6I@~b8@H~sA_`9rgk$>>nWTY7cQ#;tnTGejL81OLi$h^Ln2un{BSR7< zG_+Kf@8g{8Ung24w9c!ZPKcDY%>}BE#a0II*GE@xJx3zOIF;%>kbHlT^)gxj{5_!8 zP*&i|ES-btwGESx5GZE2~U)Xlkm)-xW_mU~2tkN8MU( zhyNHdT?fxOo&LEAcZPv+76N>UBAjRl=+E!a@MOD2e-&*W>vM=8PgTXhQ4a- zopX9NWm0mXw)OU>Z}ZO$yp91RBQp`ifUVMGeXTXl-IqbHccrzApD)3uyL6ZKx}AiY z#~52Ge7!4sjM8M2ZXM@CNnYW{Fr75XX?R){lkfI0>mld?EZ|6h#_WuDQ1n!pD?E|Z zfN#PKs2wHH%7My_jGXOORn4)Yn{Jw+6ORYp$JCY-6^2us8zLHD)Xc+AN=;oLH#p~+ z%NqHIKHxlRiJo$)GYKe=1vhyX792h7rwCYwyMCH06ujLDZsUA1LAL%Wp_^N9 z?)^jJ2PZ;rVBw!MOl1I}e^{?q&G5K5xJtWIOHWgWNlQydOGZpjQC0+{DkiGZW^8oM zo`=x`D1|zMC_POH1)ml#_W0E|ijj+x78V6xCi+MBmw)-91?$=5>9Q!OHj%mZtO{#U zuWkG>^?1C^>>p4pt88iNeO;a1E`c;!&69jFL zaZ^0WpkzE4uM^p0;|LRX;uEkc%8yIbP`P^^OSMX@Dl|+T0@M@q(-S+ydmi524&6(U z&$+{C{5ZNN6AvE67lA*j6Dj5v-2NYkBs&XrJzDP*P3l<^bSd~a;&`L^>GX)hvpPx? z3H$NPbIxC>qH66!gIgxFU%Cl6U2#lHW-p`De@sgyEI2fB@=(!*^YPCIg`hwwKeTJ& zlefOT!_hL85a?JcOVZA;9jPP)`Z*4%G3Pc5J=C;}>t23g{TOz~EZY9wnXbmWm!D_) zoua0%RFgYldA9M=wpN#XYu;$EW{o2pO0#I`xAO9ts3N9X=(2wzPx~@$Nz8@JHZVqvXWJ1(Q`Q`9uVJ*nAQiFv7 zkXFr}K#|N$VRcy}Ly501gR?G}Wth)P?n6Vk|r;zD$H4l#N z%m0T|g-ER~t#EU_^+%S1EG ztERb-6Wpjx$VtdDbF(fe74Njb|TFV ziKNJ`D=u0}#xIMrI(293a+xjV<#8ZOF_B{Nn|F13lJ$CV7kIRv4u?J^)O|NO=-w1a z2PsS5b<2UFD$vkxn|@~U7Q|*&W)B)uT640q%{sru2D6{(IXE;lxGdi^<+-f8EOC1ZZIp=Qu~Lv(l3~tv@#D zFtQ|j76%cxtakR?@ZU4xD-88bzM!A@*1@w+pSn;RICTT9dys-=03svitwP>$f0*Xx zg?0@3YPCBSy)_)LyQH=qV<>h*{M;QMVZlyL&9`J`S*x1{x~hFKrMSlteLm*gsFv;f z=)Zjt&nUz}I=MeIwIl1p{Q@`bg~q1Gq%CqVWirEguINESeZG1ERyij$7+34^sf-}6 z#c3-31jFD<+tQ2H4)D=6j{%_h=@&?6{<@gC;y9hk%(+^5{AiSp9>HoQ+t2Fo?!p*W+3v1*jUH9%*nK$tLLQj-1>TP_Rb12sqwG@L~)GnMjR{ zn^E`z7aGY$70EfR@s>)SqY)ZHHTss!jU_(F2jzxlmoa;c+VNdYZ~|Yg!aW}vSMXb< zP9d3u1XGWC>;MCT$B3cs9Me17Q8{%Mxpc`(L1gU{Xs)w9Q=T=KuP`Tacouz8KL}oF zv1&v#I-;@-Zgi!C=P7^D3myiPVh$X~9d&g-nbkhl)v zW5Lzdn^a-I8iG9T64&+yQ8Eh!)!~v>n--lD6AX?Y754?{C-92U9A+7nc!Qyxr!01*KQcx-utJzFByyN!+_It z^TyeXrR-%aZujO*TLvF2U$cxcE#@aL-#oBt^L^SMSy28M%C|Aj_}CarM9R34?O|HB z9C(pf7W1-xzBiM5}zRPAr6jaPoSoN&fod8c4#tUGjT zj9p9DF&KyV_q2}b*h)u`yP6z9*|j-nYIZn+xjm~L`xh(@O4+?Z-n`PaA}G0<9*>EY zCGX>LN%Y%b#_z7jRJoewjwakwNnW5tRVwdgYK(Ea8W`kOC6$k|SXr*-ocGbPrSz%FfRiOGWs9C23c{XS=qZ~6)RSL_pe*i%Y4f3En7ElVIJi-n&1K} zf<6MDGMmFZzzYDN zNoR3f$76UbkK^$?f$Mo9PvQoij52YokzL3xVi&XjW^b{#**olA_8$8S>Jr6dfC;~; zEfZ7m9;BVXoEle*C9okj#d9zxxda_n!zv5Zlahgb4PrN`xssvYVc^_}WJYT`6EX#T3r z)YfY+)jp(4(d~;d#{4FBdhAwR>7$fOQ{FevGe2r6w!CERu|AP%PrWGhbK3^n%W3AcrD@lsjihI$ z&rZK0W1c%M%$#y%@vP!!OAeOqE`6m;Rpu^tmQO3cry{oE zy2_f$Emd=?j#qtNeMR+`HT^YD*S=h*2`reVnU*}wJ}rM*`Lu>K|sUfcYemb+RWZ8_TVV$1Ja z-flV7`sW!R%=mOhXlCq8^USQ71v9H>w#@wd%&*!sZOLu+w*0p8wp(VE&6+lA_N=a1 zt7cs^yJq(EIn8qi=kA_+ZTl7TuATRbj`)r>9a}mso4={^Sm&#qCp!Py`AO%Q1-b>M z1)c@P3z`;7_K{DlJ-9=P!7^)c(a*59}O&Hk+Zd-_Mk$^_$${g@p;7ym&QFfO5#3|XOO z8t4TbBycR&dOTK_9vUNw8CWtih9!!HSy?KiNg7LM8O+Wyu>zgYA};1;*^o#%ESKf6 zeCB07XqEz2$coq$R?JFRDJx^;tO7E!idC~3R?DWcI#v$}F^x4qvNW+~*21Q-57 z&{(tBY{Q?q+-073`PnCU%lt z$*yD9u%ED>v3t0R?Z-N~9P;{8_8I#=yP54{&qH?oFT0&R$UbNPWM8nm*dy%M>?QUn zTftVc>scTB4O_)tX0NdSVz08-*a-WGt!95Kr% zhE!a~*0T+)pKWA!vQ3bYo7n){0$Dr=X}OJ^V%ynHwgd9{Qnrg7VLxS;K_2dA-(#oQ z-`P>FhBO{!A;?D^`yt%o{AWnvHzA8} zWskAP*^}%kHp~vQC)f+n^2ga#>>>6ddzSr*Jp&njExUu=!X97;*^k*j*uCrkyN%t? ze!y;IKVnaV7ml&#xQVB5Gq*sdrg9rkzP+O9lW*&8jjVqjIcDSfn4Q7C3$-NrRa@xgWdeM)zRwOco?=^fa*p}%+Qpm+!i zU?uoH{EzQ}BK(BCEbn9+Rr{C;kfrHk#p?T50YAkyN{d-LARs$fC7yNi6Rb)4E9*mA zD?7$c;$ALbFQ5T16QBVUgs(f0)&e$5xvY->`8|MLfVboU=Fx0KU&ooGcChX0Eaq0f z%C@U61a#y2dR(7m+oj#?CV44KQs0RCdbVA=5YKR56JS2o86{O0<5?}!s~%-0^~0qexN5h^@6jAZU$`&I?p0E`8ud3bGw_s!`+ceo7OUw7u6LrH4j39o zSpfa&0fzyEllBPik?#kj1G-iF#!gci`3p7+V>_wI!nlvK7WtooKD!9_;@*wSB#XO5q|8n4>Gz>oAUco!zeiCRq$KEAc2s#ig z!XWJ)y9l&cgY*f&Ynmcf9-#x#Vj^H13+YEyn!bLm$hTp6E=xMrnk%h`z*QXQiMgXgHz(Gl3ow;CjG9z;4|C6lsCF zk`ube5Wi7sid>^!JNOcOldWOb1wtwvRkP*Ab zj^gKEkiHi}+q#3y=jaPMdRDuFtbOsSE`N~Yh9h{aJ*e_@2UX2W7CT+ewEc@6!S?pW z&S0P`%@M4mTxD06BN)@%+ZW8G%a~?Iu!u56RN`3sVh7x#{k@K0Z2RIK+;PyeSjv@C zuDmC$r>m;eD;>Y4QhEk$cSRI;2r$_c)sOyLxFdqHmT);|n^{Fo#HwK{?yi+l0DmUy%2} zsf%fxNq9iU)zB4;rK=8H#p22z)KCZT_Zq@uITVBZp;-J1ajX_rT80Q?*_6M~-=IB9N zkH^Jv*08_lkVf+QgE}8>#`QQF(M=DvjsNo&yBxK+bOn=FrKdTa7aBE<8$0=C8dZz)G0ZF&n;+yUMgCyC&rybU{lNs>mlec=ev!5)%gl#` z5^Ab*RUV4xDdKZ{ZQP|be6I3^XJUtG~PvK~}v8T@!R5kYY zf!=asZyNGFT_8V-^`c*_99K(kWtt1^v=B{njiN0yp)`daz@xYTGQ2@G0tjlVi+Wfu zU|h5=<2Tkxn#9}e!%M@`tsB<;oS&It@^MbPepjpfwBa_0o zVV#Q$9JN@{3a>;EGLAe=HU={TPs33SWqG;6_{4DhK})!^#t1@tNPu7h5u96nE=K{) zyakJ=wyWS!3^##UQ=_+m_wn1d=xtFtQuVAdY3MRV;R7r_(A`)N)Pwfv=eeKZJA?q0 zF_?%lc3;qd#N`Vndn1jXH7<`YX!Py}zYt^W$9nk2*g>j-U<{_<7WgDyi=ZEjgyrTa z0KY4m1)iiM0a+D2jak@2cu_V*jY692pO_z zx=x}M4JkbC=nOMyhRwi%qXiNyGN~M2&=hSTmo9==z^PHJtt_&g?Ya};`x<7_ z4Ttku)}!kS=XI<|R~pX8FuTqi&c~{cNXx_dI94PTg!A#Ryb)G5D(etQUUbdU&Y4@b5Ok3mnAHW_Y!Q_}PMI1MmkoH68H0 zSE21y80W>JT?awAE%+UC$|261e8B5_= z_`l#A_%HmA`-t$A1O05l?0NxP@LRzo@zrYFQK+{HdE!LECFM5YX}{o;vqwt!Ung)j zj`^)9O|&C=MEEVjWsA@jVY(Nb;s@sucPUgnyIrdL-TXI^Hyol*Wj4e$^T8L9ab!hD zsc<;We=WF?_-TgVw?T}GxSQ}o>&}6>(?|&qTQPHG?lfLnp&P@P*&_Iwa76q*h|qIG~266sIw9${dE5);opfQ!F{%GCOYEXKv#(laXs;tE`>X-0K7+Qt7I1u~UDoQBp zg2t@OcQaBNEA>uu-G=%K*KZZI$Emae?e>M&`zl0^sJ9hpmC|h!`s_nLTQEwRElG#* znJQymgXbH?JPBVk4jMUO@BDpJTO_9x&LD2zi9E?M2O@CFx?L%H+=9D=FT%EhE0So` zGvSIbx&<*ndO|JIy_Eu6bfw_^V%(!PR^XbjbvBP~MNN_m1ELSYFSSNZzf z<`z+Z73A=_)@ncc+a#WCKr8D}mqxb=vsfo2q9RKpD^pp~k-ka4&>B76IsbLI_g2;FKU}OmGn%6d;4%l8OcWEs29Q=t)WfmMZpc>h-fx|ABArfKB9cz zSsX#sA?f2sN$N?F^)xP$7=su!NhTWkCiF9o*NL>Fr{mNepEF6o)o6vNL>xjZQ)z{; zF@Sz)el%m^YU*bqcWl8^8k15_x!Q_Z(LNc?D#E!DYS1XCR4=%a+9DcH9|*P4rMSI7uMFE3Kl*bDpRvCfg|_RU>0^Oc)d0kJvyRXmbr(?-#Q_SLy$+ zY^wkNSVsk7_c0c>WD`^NrEQb;rD^|HMTR=2Mv3mWhG0*7c#2-!^(B{VdUA;$j zEO{{DQ`JsYt}0i4TeVZZ7J21rp)@Q#CSNNJi#8nwhyK;%khE4>Yj8+w(TfAEkN=?c z@t^p^%8z=F{#DT)wHa=gdcu$BBb>)IjUnM>RWAR4p3u1H2lH^ytPIa8zv-IpBz%fl zUyR)`+7(NP?ugNjmk0Y@d60Uv&lTC@`sNN7?QGGmRvRql$Zpnx=q}cBa5U*8vNlL6 z(Js+0>=W(5t`+TRU5P!MtFcoI3C+9xw<2-;J zt%umd*v)!e>;TbT&=Gc&9m77*^Vk)73A~-v4oxrXV?IoSW?$#;nW1Ys1 z5bcqD!M?<*Q;!X@SU>_GY3w#uH8zV?BVUWO9?&&5%$5TBac={jZvqS;KM2?g*n#Uy zknRFpiu@g@^9R8D$bSI%5cf|4P60j!`~&ws1$+kh0@ukbX3Wi!0d7DQ{FrJ$EubE- z2lp-q>;+r_xDs#`U?1QPJU@=~4@kd6s$z*_L1qAa1UL;wD^&mROl1b7(m2;ecmVZalBCjn0ZjsT7Vj-$>C zNM8bs06s$BrvYE$KDa78nx?VS>@L9FfO`P<0`3Fc4|o9ZJRnljgfS0e%)=P-FvdKL zF%M&0!x+~v#x;y_4P#ux7#H>g0Z#y)1Uv;e0yqkI0q_!F1dt3auLYmiVnnqV(>`Db zJ1WQz00yDWw*oE!?85bjfRliaQ05fUj{&EV{{pEA9CRBv=r$IQxh5d38XE!!jevWG zSOE9B087y4QlveAev}yi>;ilUI0-lf_!#g7zP5p56ne z4};T(!QI2)>S1;@>f8wU5#T1k&460~2LV4r-3I{=0UicC0(cB?81Mw(Nx)NpBY>lT zVbpm6=}UmukRL(%KKl3o@DISJfX@KyK%-o6a3$!J3p(Y3PO~tp9?)tQxVe&LfS2q5 zWtQM~-1A`W`2aui#Yjs5O{miXm=0(K%tZZm)SCzB!1a8jok$lTT{w0jTa2^|l;{R5 zL4GOHe)PWqWi|l@kcWf-kJPelc)kPoE~wk zegwD)a5LZ*z(K%WsCzfy9>BeT`vCU?9sv9d@F3tJz{7w?0FMD4NB@VBJ^^?V@D$(( z;3(i1c=in7mw;yh$I<`uxIc`(UO@U1;8omz4c8;czYq8T@FC`L67Ui5bPDOmfYZqT z1J6DMdlzAKzb=)5Av4-_5!W|TnV@eun%xG`o0nHBfw37 zn*p}~4g&7NnC=GL1GpD(AK-q#1Aw1lEDr)60z3?O1n?N(akP0D=@Wn_0Z##r0FDBF zfoIPEehGLMa2)VF+8Rb*FW~u0fLC$vHC&G%|31e40f6{Z;mcXT_$*+27I?A;GT8+> zc>wuQpcT*o=mhM+^UDEy0apO71Y8B!2e=D$?grcgxEF9A;C{dZ zfS;l6gMfzs4+9cnz)`?)wD&yfy@32nfDyn)7~^Tcmw1NtiCioo z0gwnOB_t@)EYxygqQFBtyR^0Uu$$r;vUOIF0;Sk_`8z z04qmbaC9e_&#SEKHYfFA*F z0^AI^1#l4XpGu)&)QL!-*N|7F&{3>`qnOE2@cvQo{!x|$E**iC8G(cu0hf+|OGm(o zBjCglaN-C!aRi(=0!|zOCz5=_nJ>U;z?Xp1sGL%CEXk;0$SB2cfM)?DDs24OECzX5G*0t_HOz9PtO!-~MYOY!X6J_h^)_dW%D2KWNkd6?M=P<9xzI}x^1PfXww(mph!Va#Tj)qull zQFAKt^#HPA6}uHW8Tnm+J-B~4U@zbbz?Fcj0Q&%Upzd*`e?a;=(l-Dn03V{>NdWQN zDWo3*i0{5Y`X!#PKZm_XbRgTjGHmZ1052X0+j}%>viBl7vnNWMCP<_fz;r+>UR z3hBpy)5w2;^h=cYPg=`E=dlQAgp>XK!LU{v#3*SE4}tbWVh!&=s;t|gZ?z&Q1~80K zlI5Vp0)~L!p>wR$A>efgc%_k7g0Ii*t9{d7|8{@uKh>e+k41E-;eaCTk;ZT%vcx5s7vtyw+A5;hru^?V6_Xa2dG1y;I32ISQ3lUB+tX>aI)>Rs zhDD=u#&mTBE4{`#UQ%qAQcQYPB6sC@>QrT=9#@uLa%JThOY5ZYLtGX}+-Y!D06wU@ zZQY!09r?DLLSL39?RB%x=$Ycpu(=)inQ6&hQ=QdU?Q&LoQ&YXwPFJd*In%k~6 zAkO1=z%!^I%gH#=l@`dt zZD5;1#~vUZbO1^sWvHlAMHt}*lSb<*;AIAR()^`(bMo(#%~@%M0Z(?IC@rlhknIT+ zrl}8&4ljLT`SK^0O0{2|cGnf8r4`h<-Sq`FTR}Z~WE!e7H>b@8c2?d!@F@Q zaVdsG+=lY<4FFWRqlOkXMHB!7&fkGZa5mlfDP+l#Vfg7}X?#l8Ma)Wa`%Xg*66qNZ(V$-GD zBRTb<(0jL+S5=kY&h6tlnk!CRtM+29jchuv3Vl9-1LmTJXcTTSAj&y4X@MT5jnTsH z*2I(&D?u(m9N;w)9ZQVUC8B?jG)^5G$C1-8F(z=E9&fDC0*p`M335DMMkK)@bI_I^ z>ZuoociPZJJdVK<6UwyU%1X;Cj4oL(d$|#0!DT%!sc==)@fwbWTf^lNV@a_&C7o;K z5|GZzwRCCHB=T&Olb5$#Z**GqoEP0YMQ!u=t@~RRd-5eI=}`Vl)|jacO{Kb-Mc1|5 z*RtPN!ApY+sBiuIKbnfsbbw zmW8(QtBX7;tvdAQp+fIz<>8yB>AkzGF~4e#=3+?LX?Szy#z1^cRkkbLW-;qD(G^?K z;>ZBGo9puvfRKR9WF&TAR>FxXpjRgJ0{0M zsUpN|^>odyas+lA9*n1l z{JI&nj(bCk+oswJ>T{CS#tRy+F<4UF+3|d*seI{m>*wCIeYR#(y8i`JSIMSSi$28~ z6Z(nX>8+f#vi^emch8HtG~F+4uWGV{p3IOPH8WCqfi}%k2%Efk?C+Xl^{?<|R1W;) zX4b`R0rSGnSu+~~mE}_keUQ2&IW;Pw-O{n@ia;x-B?;l7_pD;HI?72Bp_{~nArrby z%_X;ntHB9Cep}FmdUU4kJL;i4Ro0*~6l1c+p$#wE%5&CLsf5&y&^eQRqrNUJ&9nNA@u zctx~eu~M)&To6*Z0B=4YRzC+@r;VKsm^f=`D)f8Y4truktd7<5dL7ANJE&SeqqWkN zs@6c}s5BDUJkS?%G57&^c55M{zz;H36qFNJsX$+iCVHWYT02w!{bsPTMJuqP+@N@} z8jS=R&Rw7S2c-qEH7d1+2#a?Fp#X{e8jaEhC0O7>kkZl?D>Pn7D$Cjs`^%kFu-i%nLAg9oW=43;Xq!m{cb@CarFOaX(+6?<|_}=$!*q?0E z=S}ll(9f@*e|mnBEzW|D-V0@ovCvq^^*`vyYR%|8a{OiLr&2DIK2ZM?%6zTBY6&Z` zr3$Q)ZIV^w^X9m+?C38cMyGAR%GnJPR^6GfKP(#p0nVjaBW6t2bMXgD2Bp+x!VDy+w9wRcz&V={_k-H|` z&7r$Shdx0re8iMz(3|5HY+Ha&4yX)qi?%P0v*@$DKYt`8H(76qTev;)VA~=*aOMA; zaM8)`Ru!vm#mR!LfnW~s=(HL&zC{3|46=fZ(QIf0yj+IKVfn#MB@uxa{-7ps zF2ju{NQ5aGEf+R>$N)k&=Pa*QjBsa))8H~V%bW)37#|2-cP4Z_->kYdH28K1>ho=W zE&6H-y~%Imt$5ql98f_DX}y8iv_uJS0W;>6px-MdSnvvul!Hks3gjlMf2zmd;7f7T zFP~Z-7pISncR13tuF4#9&{2_R%`9v76kjy3(O(p2)W=$4;P0-@7{UF%MCP5KO!t2E(2|>+a z8Yc_8jVG%o>123gz^3piQQy?>hr0r|hQlU+ z-vWhO93~KyE!hOL{Atlk@eUa`V6z$`90 zv6lFIl9pWcsQS73&9^RV-*?HDn$TNG#tQ%8zKn)6et-Fj3oa;Iw9J%Mkmk>oq>K%B z^aP3ca z85|`E{#8w-vrB9VwLAKkY@TX57mlZ{y?R#jy4mH)ou3arJW%Do;KtRJI!&r|ew(YJ z#a-A|p7BQdl$QL3lkhFUU1qA)uxgUA6QK)f9ck$fNr)qWRGQLeJf7Fr{I>hI? z(|#=P4{_H#@96s^V_=)B(lPHsc6%VHu)v$=a@x~Vj3k|frlrVMjbAINxsS{0R0JWx zVlu*BGD4A%MHnRU1h|9NpNK!IAeF>gOqSqs1scv*8#TBFZD2h{+fm_zL&Q;$HmE7Z zTjs<^PFN)qgMtzNSz#$~k~YWals_lYMPF0OSr>+mToj<=wgsss2ivALwb zk(v^rpQ0MqL_7|r01rbgp#Z{Wuu|a}K-H7WOi!VG6ki`QnS_Vp1(5lvIKD7U$wivV zRUOv2kZhV0cu&%$OvxC%OrL5*RC7;7llALYyfrGLw<$Bun40h^R8=h~H4CznBJ6GK z2LW4EWwx8M#%YypRc(cScSUxE(ZF2X6_ud{wpa;D#jXSKh+aa>sWBiTmLXG%L1NU1 zK?sWhq$>|sAl6lhB`~2Xii2Um#uMsMeMQBnMQp833cGp=J8QIdnIvUSL^g(8vRhQ^D-2s@Vl5%apdV(gu${ zId57)VIVJA?`cS?yXejpHR~2NS^RfyUU0*OQ>R{d!-CCs`Ylb1*43=I^P;*_j*2#K z;hc)h%!)aM-nI&dbZu5;t0yZk#crPx$nvySX3$*XpUUNkK%5531n^r`Xw3%LsO zEGV2&lJ?5V^-^WY+S~hA2KUZx-Sc>V>(G{UC836lTq)O(nPj!{XUgW4r#q|X7W`=& zFwh4pdRYEUSex4>S)1gq*)Y|x5Y!Xvho=p@nzRJTR4ZCRJY39m;&ZGJ=fp>-N%AmK zgj&KPIhP4;nBZ=ZeCV5Ol25*#Y@$p5^$nXHGliE!GtGo&+7Zwd6?ohlRn#xFh8-RW z#(QKPb3N(8fOEp53HBDzpNv+D`cLvB&hn8aZ-4Zk-)GWIp4hiLQPM9aPm|+Z`LE=M zRae2%hL?{oyTQvB0R%NfD_ml=9pC%md*pnpE%aAgwZlIy6Bw~ZUKn(e9 z2EZ=)x-$a=(xSJe+3(=_TkKbGH^XB!AiUy@p?5Le2j^&S`#Tct+>{FR+O*%`yH=wVoDD>xKXE}_?6LyCR zg~JTh58_SOKvd)+h7TKDMJqiVM1$ThKX(lvC$i!OF?|T8@@}%8|U9T?r7r}a13}FvL4?C`L^*ZJHXixR5{V`r;4aW;V2Bg;6*XsH(coQCsFssNK^xZAF7Kqq=MRkC*vc z>nbugO*0-ig~5^*;3R)pM`t@3fnZE`5ISj_=>K^qJ7`hu^DQd*JZz z2Ya8s_nN-SXkWzjN80pWZdkkd|eL&+d0t=3BP@iI*Q+_sU~8ZK(Bi?!5IE7q0*1 z_dAa1o#naQAQyFPzy>Ag54%A<44WzeUdWFF@o@O#VTmJ>74`WujI?nFFBpgh)4)BD z<+#YMWW)u@4Hv@xZFrv%WjRzTEjCnwprM2ukb((C$!3A+QjFzXa_$O>tDnco>2Q)O z?Bw{75gUY{R1b%~@>Rtx9DQS9Y;}I{6wv}A|dTQj$8=`9(}HTL*)z9S~?`&+hN z^b$ARc{ew_xG|$-y7$L=OOhqck~D3{!|OLcx~(q8?6SosV4Z+()OUez;_>B0#QSJB zKL*}<)ccqa?*n5*f_dbF{3r1~%obX>SY;N0J~-S&d-Yu#UJQNo;OHw-`Gef@;)c+D zmFsTleeb>ATh>)dZ{8mI{P_*)LmQ8WKL1JoqdTUZSu}0OqeNTc^el`xjtvGt>~lo5 ziNmu|62c#XfShVXx5dV%m;%;sG^!WyQB)h$K_T*=BUNM-xB*O!Ju*?662;OI!2+@= z@T1BaeUQHrDwh_XS%}@0GogpS3f&tzgTYN0XAD~%NJ8^Cy2550Nrth7`^Vl1K*tb~_Db`^h7L-#w*m4vR{a0LS7vN z5b?M;qk{#~c%IrS=xLP*eMQ?rUBOdu@gj{-TZ;LTU0hyKh9eVG5eAKFtFzWiP63pd zvS4ISOMlVz4!5MK+;R1-U3Z2)dFi75U-OLMk~^>3>{|gN&F;vZKC`3bf?L*C2QIqx zpH|bKvusC4f1Fq2NgiJ;_R`k;AEj7LR+l-papyzp1`qF=7N(|pv*5ZP1oRWOQO2WM z)`%`)9vsmbS0<4|KOil*NN`nH=_mz3RX0eA2aL8^yg~p86$4sBlz`^Sfgj4LiBa8x zp@|>K^v@FOeg!(kf8sHvH%gTK3hIoXq7DQR4*5zclKQk@TxS<2-9iT- zkUBBNDd8rei6Ujg5q2f$EY>|0j`(~UWm~+Y7!h^E*C}!Z_li@9Cx;i{gbT3)f2F+5 z$y?K!i@7J%ZYj2T>TUc;==T|Uc5`g#$>fT{j?kmLLta_P-^xiJecX|+*H6hCU8+m6 zB=WD*Eu%LI8bjX7^aXY`YYphc)**}TQX4@m!8w>EbR3xraJBzLEz;cU?+HBBx2nP`E75Pz2tAloG zM8{FgpQ98|2O3au{C89I1gXHhC`_gh%peiwCu@-CMFb!nKa znVq(jSOk_2dOEzm`8m?N#*|p;tGRN^)aj!}m)q@<{_bkdICE5eNQzA{j%HvKq_asD zS=shLycIk4`uI4wJ5f9(g0&RH2$q!K;n3byI^D+N!par}h@VJ95)*=IIhZG%s)bF2 zi3$V&CuA85124jQ;X{UEf^@zT8@%K5F5&rRyVb0h4#Pgq59XAoj?R-Y&BxS-RBf#kpu31iVQrDI$-d5kMG<3&BqT zM^YhKoOdPTgh+_Wluoct!Zx=H7Iryp5Mk5zmC&P-bnwqVXll9s&o>8ydk5`Pmj#0S z!%(XF(2U)WuG;YAo*8397ZrEZIH)&Y=pUN3ph!C2Y2Fx!_j_H=R7+yQgfoBHkZiPU zFs!ohKony+Is+KXa2A{btq=QC8ZHx=gr<&ClGaoBtSha<0;MqS%7U!nz&Gl_Clfe9129+V)G&~OUD$Bz z2>3w!6=MOH0PWviEqrh)EQ`qZ3TucQc$vf*w0G!HID`M`_IJJ?@U(51o(12gJT$T( z`_m+E&cc1oy>;oAUcOIC8+~7WXxiZI%cc*l?@TikyF=;#zdzmjk*jWgQBOZHfgXDW z@#^RB-SulJ@|EjxIc-+#Vn|UwFF`mB0YGf$5Z7R8*i*%XWt8J{=KQ-|N=(F+g;yp_5`t#N?htr_EBMrkurfz=H4Y%T zGv+n>s`Fjm>hA9?SrxpzExn}0dwa6kQ{b7mxFnG8$f;U(&FYTZcFsu2t0HYeXRHr` zx3uhgbP^tEIJg;=<0-^AP(f~Bi43E#epRG7E*USw6Hx>b9dR@Pf=+O{1a6|w6dWq^@jtwb}DE%kuR9!l;YP#1MTXVUJOgyil7dxF^o(aoj>y z+*fuMU-V+0 zq<>}jVE$Z{QkaR}=8QK{P^CK&C5PN0}`{Gq~p zL{?(ah~We7%S9QmD1321b#Swv~5)7F!PW( zDU_Sy#CpTGIhdMu$jz)bpfj6rzKch>GbJ9Y1mhCN79&PNgyO~t55ot)KyQnzNp0?I5kDgU~Eo871WqmLBRVpu^Q@WOfID^?&aS7o;|?kxFAkMIC7Z}b_KuaK(Y zw@FBkV4M-&#TW|V?jmR&ey^iM<@sxxJ8qXDBQDlv*dCW{|Ag;(MP_L!e{)o4WK=u; zW=fYtbdH67FPp}0#9C?;=Y+AU!mEt-of9Dl&_B47{_YwPBb7u0W)?ngEgn$`B`l&& zahA!ZGpGBH9|wj1q`IDe3R@_K+Q#mU3R^ib(G`rplZI&sP0b8E+_rMhMTo{zG7pd_ zZ5t_EEPSjpg$0IvL07&7A;#x2Gd?$3xE9h3?|ptE|6b+9JLhFA6tH>oIV&v4FY}hU zvog}GW@9qm4#?wq;$0CtW;fM=BS?M-Eofp5qI(EgLmyCdPpg6LjTIonCBZ1vYExzm z*Jxm}-$F%>X5jyr3FPc(jM<-)(SZ>z2+dydzN2he=PR0o-u_hy9!+01+ z5Ya^gQ(B{hUIxJ;QT&GRdso=ZioOR4MhPEzGKjFE9>+w?a+A-WXwGS!y{K&Z;5=XD z{!@>&HCMD$l)4@Mgv#abU%ve2b(JA~ZFj-4?Ja55R!^0)*k#CWUEfkMctJqrTQbjT zw`N*;=g*tE;ij(9?Z3<~yk2E>7o?TpufoB5fK9v*D?OF@ctXIOW&_t`*?eg}@*oU} zB!8lBY)m2F&XI`|hACW9gD8*!h^$8ZBW|K?2(X763tHI5^a2kBx^V<3T2Ve{MR*c2 z-px>|L%0dUT9G)#lPlw|1Y!Fad_l((arB!r91mA4OB#`kgRp_xZ>uEZgd53i)C%j+ zNWat=g-?h~*cdWQI)O&}9~`k9(-3aBAm4%$aZR?(&q47u~w z8uwH1E+UXXaupaxT}r|S(jtBe1+&GbZDfmiQx#&v|3CgEnwS6IkVWz)e<;UMW+mKYLPgEV$>9^O_;z| zCPe)e5fNd{@_)M~9S8%9iUWNlU`QLDzqZhlu!C_P7mP}E$*Q2?FbX-;pSN34k@yl~ z99Rf9fQlsG&<>@b$GId45dcgpcE}Czpd-5u?HM^bYi85jC1u2>fvxwfY~SRE53R|n zD9AIp=S5i5W}E9Q&q-|!Nhk0XsOfY z9>sF1!nI_QE(qKq3NpDibly2OAH!@}V!#_jc(W)X6!2~q@nQzvHF6p5YjSxHRINX_ z!ZXdK;fdz7guH;){LfQzrCyii+*#9g{p!k?*qf755;RG9jU`iFKZAF(WTp?@s?w=4 z@g>aJYzYqr(&x_MY{C3FOXe<_*&3*GIjx9WVn|iIDvmI0#_Xr#?Q30evLtDzLCpYjPbe_M0-+ol>aNIVb_ zlN*liL!vP{%0pIe>*U5T5DZ3FJaLTUjh;2o$?Xwqd}DCm-kyfhYfLPAA_jD%Bj%oO zT!bWi&H;9h=iEcYrW%<%;kPuE_QmV3B6tJQM4m??55FXkb5^j6{3PXV$Z&$Qohq2j zk<&mj1&MLyhbl?@gNCieyeh*rOF!+RgNvtlvnsj=w-0t#H0?dMYv-Y@m7OzO`AJ!& z?s>}^w%o?2WtL6vSnSQnHWtmRYg|}j`e1f*^X%Ep26u%j(`LQMX4e_~MRRs7D%6;4 zHcN~gXRGdQYq@f5eL>rbvgQlYEUgumk|LkMd)@Lmmo6&&`WLD8WSvS@o2F!%-L}M} z^c-pD?5Xvwt@ZZe>=fV~dmkz{G*=3HQ|zjzu$1Jagm|*0qLD_dSDBkAEUuVQ;J&cg zY08Am7XboV#iV5>mW2X<5CsuLA(9ki^GMM>J^#dZDdYl*MC;%RcidE!GGcQ{DG&3S zwwG`$mq8+hE5X?%hQ@5D67CE6@D1w-Do4;pYk)0OdHIqBmvCS`E^JY;^ z&AJ;FjBb#AIBS6~8=FJudNsOvOZ^VkwT0~>Z&zq^;on3(@l;4f^iMj|h^U=dR>Hcc zO*BG_l>+jZ90MDV7bQKpN4C&xwtJ=p}t7hh>W~6Z;|(qy+}Xu{X&OO@VQs*v#%oaWO|OWq+fd zB;NQUOhlTdZ5Em}y{&=&XIBz-@sx0@<0Y|&7(QJ^b!Y)cMnermjA4AMWWuI31vtNn zwd@7gtbtIEm8@>K7jfrA;s4I~UT`fYwEcZW{8N|J{4c6)8;74@5Eo=!^PZksjq16yuV zR%Iy3lEF8nCWgv@LU6$z{5pcH%(^B;5lC+&kPQN?iIqjWu*6S^xCA%2#k21UEku_Q zmW}$&uX2=LX`g(d@L|3Tn9*E5%f~rmyl-SnH6_Q#%3N57c68>}Yw*fI9yVDdy9UlA zdPc_(Px?%Rzhn_ErX^3K&7Dvdjgo<+c%L*<(|Gxa{WImP@-Pp;-zbV&Y=Y1{2J_rttDM&6Uw(*tdg&2Mw6 z5^T20G{4i>JFloI-&D5hhs#Kw{&fBvO1fG-j?rWcOXwZo+{XTo&^tC}v!o;&2-`vm z5w;z0df_?=y`%UPSP?*EMCU{>O?!Kha`YS+KtT${ovok9%Jfex3v*FHQdmy`HK66$ zI!7r#LFeGmQMl5#>zpV)@gb>67)-VYX5g<(kLw;nT5Wof(>O`}D4r$prV%G-RtAz& zaLOz#)s&f%3BSe2jZqz)j!9!&Smk6NlB^D+0FfaCgT?)D1OX3~c;t8~DjW_6M;wu` zk}-~3GiIsf;^WuMo_)>ni?==h{n@j>|NOS~t5&VwxO%lJW9I%BF4^_cHEnIzytM0* z7xvFQ^YqOJ58ias!GkvgquWDIsTQi7B5q@y6t_t=n~3JZJGO#8u-;X0K@hjGintBD zV?<6=>SBfTBI*M5gQyGElPn{kOiGA!R{8mA3O*1k@Eo-x;kU|fuT4Rh3EdK=sYRL0 z(E3SL@sO8B#|?`_EMLh_WR)!BM1Vt6f?q%2BuD`i6`{_DgS{PT;g8r+tEm z(PhDbe@;jHCj>53NugJ24v5$q1uY6^)t9FxBv*%CoD{cEC4BWDKUzIK61YgR=cneC zp81^;x$rm;TMI`ngp8DPK^Y4-3mIu)mK0-hQdC9~MVtvb7|3*jTC&q#9dV|zRA2>@{H1PP4jYx$(RyeKS(TLs%f`r#e>$&yv6LKHTkO6j?(jLS;J z5kMob%g62Wv_RQ6s)%p!kRZdFFEw&s;=vt z?#)$MO@;#3&K6aI&X8{EUA#K$uPKI@J)?VpDl?*&b&#(06uqpeI@c+Sdc+~WBI*zqxOE!?x}+lY0t$ zB95<^eIjer)y&0{=-jhCJ=J0|(!M|52yk(ikmeNC@WpX1J8`UpJT_UvmN~A$08my+ zz=(Vaim2gx7ZGQoWKtc3zSSHrB%M_kB49#Q5)`D|tIt!F&c$#I83h!Er>LB3ya8&+ z30V<{gG8G!R>mppfKTkuA#1$BfGs%KePkJ*yNR$NC0?VY-Mp#zPE9#B_9&1P2|>fc z!n>ecH7@b-kx0`@b^aH=y4pS-YjFSGMB@b64YBzne z9G;WpMnR$xJ`owQPW|I$VI&ESMr|VtYeE>6N*$_7zB&{j!}oy17|48ul*K_Sr6Q(r z&T8S!4ES$|*q2wpXmVxY)S9yUWZ(zkrg!D@@?+J>Mi@|?!hljGjBcFWneUPQ^7Ut> zwHbvlp)S0Bp>%+7h4UV&R^Uq6HO03}5nLb-mn57K8;Rjvf9N6MQABqA4bex?<9H-? zO>yr$JN|k6?EQXxC{KDa^g(DN5aHKdZ^(>`b0puS^Vz>pC1hGcg;o9K<^5IscNTOD z&WEYqts9;Ptg&lH?ATj zU=cd$8Bjv6OwcsN37ko%R!GK@S#thkD>WRNQFwrefN5oX`{7^CBK6vMlTM$RxJOf9 z-|sBc?9yi>=qw2v)rI!cp=ZyS(seh+CTq2ZxSJA7K2asut)arQz9~~ymh<0RGJ&Fk zvc8hyl@>3@#FqLy&t3lNawl5Hyk(QjKkB5U4p{pe&5%EH( zL2-UX(Q;t<2yI;xAD{OGOTH)tdGQ~eVS!sr-${%dVvz|UFdUqGbLcUik5ep9Ym(#k zCO9ncjvwPoLvJ{96P{MeuO(+0KeQM_siS|wA(lFWJs}hlr&t(+ zoi-Emrmv;yQj9trV-eg;wy!Ax=N=}qGfXd&{0y810qBN2z-Kq32yu+p6wk@! z1VcbX6JIbo_vWD@%8>zxN-SgiLK!%h6AR)%neeXZ;I>g&5pX1Ns)xQDDLM4HzLPJ9 zR`6tZuI@o=BF;4()D@Ecx(GlB4Pmy4^cEGpZ%Bvv zqEx20sG@p=CPq3Xd}=-51e;?Y0G=F7ZH$qEB9S6gGVJJx&)nghf%rm6nNuFJCWYqm z|2p$(ijhANs+Y6m8ywXsqr)Ez8LMnk;|J7)9_L=taT=rvC&l%()nz5{#ggd^zmUB| ziQF6@1$;xBwG3>QEF{En7P3MJVGj@>KnM_)QkrQ~O4CjYBtwBR&;kt% zw7|3!7)m=Wlc7L?VfuaTbozBVblPcyt$hD;?|YIg%Sq_;H-#ek>E83+yZ79C&-ywQpW`ZY(t1 z9kW`a-HU>;bJuNd9~w6l6cyf@*mz)hd}?wM@B3uxTkJuM6>u-I@?&U>IHH%BF~rL7 z44J(1#1Xw*35Wm5-~JZyF!$38^k4>-s53CIus@k-iq<&`k&UX_E+duj9$^N+1eh2; z?l|#j=*iy!5~R6deb5Y?t|{l893~f7Ot9*(X$Wo41RYw+?{9eEda;tiL+0Mu|KeJ*4PIZbw{n%Sod&f*4S^jg^zu-qRH?6 z`eGWb33ijVLsNxFzeP#pZ59KkkFYJFk(RMCc=wVB-qd0Dhv&SN{I#S$Ks3S^9wmZH zSojz)TK|$ykK=&hbrVXsD9NXOJ_LvveY>&cWNg*7Y2tGva8q7 z8YDqu&NiU*P|WHs2X=r2<(V+$UDUAVUvXsX)N{Ex`gnBv)YI$NKRq?`pW44uAgu#R zQa=`7YhxT9zaosgDeW(4YIPN2uvN#} zGKK>d4v+Tbem~@2Z7;-J2)%g{y9T^5R|3-@RIY zS6TdD9fOGr98R;{VVrqbUsmQWuv{2jP+O$kR~DDw-gUQ69&sNbJ}t-d?$W)8x`F?Z zba!<&Hxk}cImKEzEGDg-_Re?Ov8g7)GUBR8p&*8(q@|3nJ#G1eb6SrmQj8zRn^!f2 zWF7*zzheKJMGD?;#YZT}5H->hMovot(`D4bJ4)L<+^k~T$xBKESh-rDBcu%r20akw zDH=i#fH%&Dj8|L-tb$;uH@bWoyUymVa;4rz zY{O)n{lQh!R5?BDTM#dQR{naV`>l|a{DcuVKFI}{FGy7Q-d>}5r^BR%KUnJqUI z#e?2w^`()H;BQD2oRpr^#^G!W_;JpI4$gM^5R8lwYY(?Wv>?H> z-HS8~8X~U&0w{^1hA`Z+?Kz-RuXsK89XVV(m1tZW`NDl)@B(l9i$^|RJJrnpzSmpw z^Wxg}>go=H+3%<(tV{X+rpd?`KKt3CVz1}k!=JBRpTOVuehz;Zxep&cT)QDr-4!h< ziFQ@jh))q`tUZh<%^K;wWN{6`%PHNlw1hIspdEAlgZulrg+(YC*qsO>54<7+*j;CY z@taWHLwH?ES>n8_s$lK2^b$m8AXIHuZy^0b)&Us~3=dlLNi4f$j`l*m!K!90lqAAx z;Ap}04e-(2i)50FdJ|`3K@D0k5i@fk&y~ngdEgELuA=y-74W33zP*1;?PYSbt>Bo) zXEs&2AGCDVF>UIaKc(Je(bSu%*TkoMgQhuLlUmcZHX2*o%Dz?+oqpr!xkrEYv!mw{ zA7*3-gM|3oEVLS7^TqbhrBPaLX^Bt-8T+z-!qGrdpxT6FMdIMhZS9L;8CtWf$BWZ6An@lTh8fAE;b2lp z=L|yZPy|23r(zZKpRwnkVOx*u zJv`RG)b5cV)jh^BtuD^Nw4#+%h#|%ZOR#QW&5#FB7=ud5-a|k5=?B_|(2)M24 zdjA-4!{iG=CJSo=i(FuBWPxTNN=Kx05zssKQN11qBPyQIcZ0!cK)_IP*=b0jgo;c^ z@!Wr?B5|aNQVA8>q*a1M7bR4E?s=X z!<)B1a{bUm&qYR$v&ebNWj#~9)eg7wxgFm=ik_f&^xL4Ket&+)4evg%e*FXQ-f+&5 z+b$lj-*EIj+Mk9s3*}pN-$yJ(g0&}$Y9p~|BvG4y?83d77DQ2S*~JDvZLNayp`JE) zIAt|}^n;5QXjXzR-6nx>TV&NTLje9GrO}IVm==coVytssvBkD8E9a!z-dfT zxmk}hixNW2M?V1(=n-nPxq$-NXag3UM;}ychqDS!ZDHjLaV08Gh`Ek{D+(yWT)y@E z$|{@HTNg`It=myGao0s*UuCGOx&#>Jq3(NDc5fOCrd}(nFK+0nwiIiY6qc5`%iX4_ zRnkC(lP!)SimE8i+D zc2`^C>`>mzm=2(|l>)8J4vh>LcUf2gCa?rD*C8C$f(>!DVQh&Xv8th}NfMcD%1LO# zA)ohW8h96SPaFi~mO43ewZ@*ZFv9DW{%1(unZ2w`LgjI0NEWSKvux?&MNJLyShOw_ zsHrY;8}&SQf_CrzE~Yj1G76|5JP%UYDd-l7Hhk9s2$@EJkQpeTfwUoPV5(3k(jLTi zKa!6SLLKxHc#VQ^=aS4TV}i%W1u59K;ow9P9}lXaCu_v*^Abe4E51kO6Izv$QhE2f zwsx{`s{j*0#wDmLVqiz8G zS40Z08(W>(0oiYpjqT+ILvcf7mHgAwZFegchkxO)BeCa}z~I(o-O~ORhrPj@nco@-ENd~-iSN{!^7Y6lYV8MlirIq zxq5|z>-r?@LBxafg@Y>JByjxJ+|pfH=M9df0y)yL`IeF z?(?-riquc-#wGhVb#!dnzhva5O`V;aZW{J=))yDocjBa{%3j@g!d21)ss#KII7)Wu zPKqZY>*Z`zC4MH5fj$nlfpkJ<7an0Q5iAf1v!pT7qJ|DjiyGoO7BwtZZO{~8?S}js z!YiL`M`5-i*hZy#0F+CgoT(s?x<~WgbdC0psmI?>y~z6BXA{$HINzxH!vXm+{azrT z-AH(R3i^oNzEDVJ;zyksDD3Kxd>kki*T*C~9HJpF;y|1R{PA5#voC*_h|E)dL_Fa>R3PyUYNd zXN}^n-i>|L4egzsP2-!JTGoto7Hul=HaH4G&dM4~MN=@+RAzKn7nhi9WhDi!%S_En zHtP5KJBR(fYf4?ioeMYglsg(wRlrnT>h@QZR2J&)GCRwirGBT=Ta+2uC$;y;Cm{Lq z+&-{0^iIUlsdfe)+=rn7Q!odK_G;cB-juFkYE(xBHx`@V_N-@wMj)R^O?&J7&mMHd z8+0$G{^WK2z5?-<(pDbrg2WcKlE-USOQ>t;aamEwL#N^S4n>&75Y7zdwd3eSoF9UA zC`=wZ6z&GAKDkJ>aKdumXbi3El-5usvRW%sipH59YB)?Jt4C(*Qty;$PpUtilG%62 zv{wm(rvjpHrm#Zuymx?7N~V<%?}7aXRzh;n((clMPCC3ce-TDuS37MilbOBLb)u#+I~1`lRuI9E`7seZi&+K?Igr+OD~TjMfMtVlwhV zW?_gaI3e))^S#!gh7IZN{c-A}@1{OF|B-7JF1+TE^L0;reDX5g-6t>8v<+VVh1DCr zd|4l!yoX)OQkpp45+|rq1!dlPCnUne3zj!DtX$ytFId^ouzZ1EUKN|@uBqvs zh{cxC=Q7&O4y2C4z3+fm;5EXZ09L#i9#eP(xP97!A@Tv~Rgy~>F#^exas=8Y_H@R> z0*VMqPZ(gTXE#Ke@!WQ#ztrbz$Bl#jtR8d&8uysmn(AucK?ucyr*8AjjZO#gB=n(% zX?dF|XaLGXxILkOmXkTKz3nA?xu>*f!Q%J}j#!W19vLykTDQ#)%eE@K{bWq{^#p6*`K754}MN8}h z7Lf`v>oA_TEgEGfG+|V=_=f|ZCh~S5{yRgV6PU8v$$pGw*N(s_kMHn)eY3mBSz#Fb z=-KV59<7ztwFL|8rpfQm+;@=Vt!7QH{Gsl9;I=C1mU&`JD=~T5v89l{xqO8fE~Eow z95E&Md$w?fIg^BRc6Pz1!fc?%J__e%cJwK`YjodaA~CsdbnJ$yL}KcOu|!i-qNA}< z_r2)Kt47DJSrr39$=K*sE2F9K(DLPjgUgo>;Vo~5UUw_>I*(LOdR=`nK-MhvL0%jS z5jwCIH!x_P5G9x==wbP;7jwnV?lC)!T(d(&_4%ojITx?xnqBEz;=lKN=4`2~Uy1RAX0H66JvLIZWEKfAWqv4P+9h0{%Q|*IeHq#zAQ09ia*@g7N zATW&~VnDutl+g?)2u@F(307QKg0KN%KCsPOdAU%q8~8PVQZ*C+L>2*x9$B123T;sV zluW(gW)oaVl7Akm8G1yQK%Yg#$x@OvYZ&5-g}7|tBSOzEVaeepSaND@QBk?X<8SrR3Sx<1?sB41 zCWeGAUGquTJ5l6}98dkG!l!kzkrN*e=Bzhpj>rCbW_6)2SXX>NGf_F#c7TO1v=@Kw zb0w}R_KzE~mt*QjrMVP};f;-|=pOAeuS;?f)#lwT5CUaX$ zV?(wyp5~76&Sr2&6SzYavK$5Dl#z92`a&qGemSFbNWp(>WRuhXRDyh<*YPMY0p1P-DUcm+04dT zve``j!&zxItFt+9<}+#L(@IgkGd+u)X-rxnZP4743^^Serq-@mv3z(i+1t_H*4hxG zidc>@=U8Ts;x&uOHnm}dSuG{ZWDX&+e**SS3_CCtEmm_gN(IAQT!)2eGME(`j~>n@ z1jgI4wk^qGA<@lVh%(1;gwggT2M|w4+m{*J7fC69*}LFmn{yYk+x}(mlJ8w8VfQb4 zmwfL+LEsxtKQE|q+x$r;Cf{x7h}!ZOxQ*S8p%P3`2oXE?Ru)B=zbMbG>^h6iZfVZu zIC?XXkt|jn@c*eEwaR^{=St@sT=x06FwGJCK(dr2uRHtuNnlqL9XZ?k0;NcULfFbt z1$!EOn{`y#DC`~oBmYY7aU}|yeNrX=3`g2@=_fY=XttjI*ppxXWJZFI-_AK1A-AUf zMzaY!3e^R9CQn@uiXB2-5QydotkJ;}LYf;Qnqa?G)mANsaKOO<^8sbDsy>ov{%)iK zp>Z&)zm7b)s3nr`E{Ob40?w!-S!G~J;P;{r%J{()LXuG2V_Fn2=T3$KRE*pX&K?wL{e?Jp-SJSY9ftmZ=4w0xj0cDtG@FqCYv6JC*g*^22o8!^Gt3v zkyEGevXbt*a7cy7yaEHdksXkvJl8GR*YzKz+gsVanfCgR)7Kr@*9{fv>!w-PFG*i7 zWC#y}5BLK-1gdIDPUIwgm~>M#1(dRk0YG@!XzM|u5tK2~8a0%{f+d87twmY_zTp=- zwK)!+aOeoq`;%u$N^}ROVU5wAivdBY z2too92C(7Tbl7~41p6w32?!<{(3(RHzMY9rN+{z@hkOhUBI|>APx?@un7WMhfr3D1 zf$GmGLIH0$<$>5)C6CkQb=d ze{=efGhqui{zA~%fu;WcvaVai^;1vb`af~{qP_KF-u~1NaQ%5sUtBlQbu8I75Y=P1 zAies@q_a{4VWDzn0Tqx1GKi7`0;GA|T>}QE-9aR0V|IDmmBNCMHr4Pe!Z`(N!K^X6 zpMg8I{os4ihd#)N#Sx}OdngkCT;Y2V)Gg(HdHX3)p;YJv_{<(C(^{kl`GXvFnmcgF z8`ySCDe<8c{5YoF!%Kazb5jp}NRZl^c6l$a^ueos$Tv(<#gEh-$5XeU=!cj|-6ecq zZo-HeP+9C7e8xC(IG+L9y=dQmIq!8#_H{lpqP>;9ooUbMi|dZ;>wLz?aIpLgVZTdpYW7OQB!xLyxrmM;fBE0tPL!;JHUVP+&I${n~jc= z0%zx#(V0aNVT#}YPa0vbBh-=fMi-Tw;`6DN+ zkLsF*5;B@8FCdLI?n7I+`mq;a55RUnmH;j+U>{I0T#^tAA-U0MJx^P-#3a#vC~)J0 zyh@&$0FZ+m&sID>2ReQlRc`)$4stw`jv?RgBj9rlOaDm`{Qb%9PGD1jpQT|o%-?}5 zv~?&0NY%Fxq`-?Vh|qBg-vSz=tg&O+MPuu3n~Y`BVLKZl9gR&x8yEGg?rUyr z4-PD?U3$szbcg207d~_QIPoY)4%NK~9vuWL7^DH|mz;NVukV?4{fTt@cV=CGDt-OK zS=S#+U*DO1UGGd^|3LZ`?ysTe(J#^Z@H9XolYf~E4G%GC%{j})7mo}r9bTI3?QCyp zZb0qKG#1_<8!V*r(FqtJ3srpNt-4!8Exl$!1Icq2;z9lKamCZ8Lw?Gtp*d zGZtz273aW&5FX{hyR;>-b!xe< zdhO1ulV5qPxP9GiPhXV{cl-L@OAps|KeXxGjW)K+5$>P)1xLN@s2c%7SH-@%g$Em*nNsh7aqTT zd)I(KlURAdLokJY+l%GQ* z$~p8AKZkO!?~$^v(>WyCzccGPoj2n8PI3Lzx4?Eku zasAXIxc&etE`=n~{vGA|_i+7o9nlxpchYspSRIgh#&pL}mt{!0Ia$!!vY@+VsC6iY zx;bete5?VILnlXvN;H?coOUE=A?7uc%SARflFCSOhd$PR#`d@~wl+k#C>`lT2|g1J zVnCx4@?#&8%aM5`Vu49Z6G;~$NubD!Rt6Wr!w3$d#SL|>kn=ah!a5*ZBz8$G|{{Ha4eRbu)jmNK?Ji2d5M`Fdk#C0w0~#Tb)qk> ze>m$p&9At=ldeN9StWf2F_M3aZ(exi`vr0|;VM!l1|kcSNc_N$O7bQ*>2MW2Uskk^ z;$UDb%Qcwh)*W9(F^;S^*Fu{6x5oY+`$>*TbY?2&0=+}=sc&cxX!qg_uLqxgG3g8j zni~W4LGUQm)G!&+8b${Sxanj-$k)PT=rq&51B+T(UCaVO0+JL3Feyw6lOkd-PS-W9 zjSvJC$>9h`QYjE2kue8$6+9|5M)0LzRiQ6ZMsEw0L=BY25VA8gZ}cxBjIY~vI~?^* ziQ-GX+CNNKUk7%^xte(I%u5$uQ;>S8{t+ANsqNUkS99~wclHqYS5t8bl*4z1Ubu=N zz#4izPzzZh1kGZ}AuHzUcoa@e2xbBaf>vFH}k+QE7*NOJ;%(_n8 zC9dyOu5+XWPJfEi-y{7Np9&5cApeShnCDI+vk%%@mAAII7FUY0kgmuE%3)SVy=6FJ?HmqG3XOfT65 zIEq*{SXDNReAZ2|MZ>NI_RQBCKj6nS2AP7qf{GitF!FN|$Yx>4*A8|z7R`kpGt^`Q z$lJ?8-qfZ%=wsjoF;_H)Vy<>7b0x!C{VufkpcH4@lEq94BleglQvxNBuu7#M3-T-i znytoq zs?nl!GRT$iU&{uh?ohRK^{&t-$WA=FFR9%maxcvEMbv?=g&t3&F@uyrBN-aADUqca z6Ugn(U>+iiP@Cj<4iOj^VVnnU7Ic3toLtyCWvMZnpk53J2+SpjluqG~q8jEf8qraQ zv;+jfsGbJ~_)RvEA@XE6xjXcL2sogG=xTd>SLcaXVHg!pIW9GZsAqe6L9I7*X#<}^pm zOB#_<>&}j<9u`KxT45(i)5kb~WoRfukn{6!h?1zK%VrRYnoBz{^VbE*FikprOY?J; zYlg>4t1M?K6{0dxim4?-6dr2X5!04coS}M%%3Ew}ri=)7oB-7_=k`KMp)jB%^OOAa zfo!aKiJuz7bY0pb{TiP@;R82PeQZD5PkEWum1;sag7XNMMu%VgJTH^@u#-3sb~RZ1 zMJ6|_!L-xDz$N?&_>Kx^*pf`MIgX4Z#Bpu81VlTPxAM{w5q-p43((ud03I*NlzT{F zOzLx(B>Y1A(mW3&7?d`tpf7?@4U19k;;Os_F&2JhBG4?(pRcL)FqBzu z+*oH^cIWqRqAD0U1v19Q3OqeQejI6jqZG)%`iD--jsuBG13d+Za`=RjlFm@!nA9qQ zTqZvSau?4};j$ff3-J6D1_6s8+XX;7?JmU-+gq@^IHV0pqoa^Y+NWt(+AU_7x%525 zWKLmdq&AQjf(1LD|tB=t5Gjeaa}2MSkz@#xPQ3+_$b zl=_JM>71X3?Th3`@r2D-5}$?CY?t=(_eUK%Bao&QA8K&{iru4D0JxEt&{N^L34M%Y z+0v}v`F1J=Xkd_lgDm_&){hieiM!1iHx>K7-Q%{?@QH_Ksl$=osn-q%E(2`;d)<%4 z9Pycmd`Z`nVS9so2X^_Z;WE2~@AAUKX<=XHyF9K#6DAA2(1iC;NlvVJcs`AVI$oid zjU`c8ifj$Qr9tjJA@@+-J@OjyeZCU!l_nOS!=Ie?z2%EW}hfO=1TyjYkB?QDt$Ym2nG$4M02!d$Tmz7Lf9pCmd z+qp`MNUFf%G-s4}h<{p1yo@vQR|@o&Rt@~xCvg`3{2L`TveAh+d z6KfWh4*f&e(-V*?N7#>&f&OG`3zG&1k|X^i-JLCct$h>?Q&FbW1WK?(0r5D^b#D_S zB%fEfK`_(g{y+pF)0n}tA)84S6;JdQ@PNC@iqkIRuE-cVN1K@GG*p-g2Q)M?K?HM` z^;a%Ql1^X6gN^4QX=8wGp?TqyjAmX1CG)gIiU-*Evx##1Ih{dt{I61}LcvkJJ!hZ_ znU%XWkU#*CKHQwL4iwg>PYUHf2W0gaMO_wIM3AVI$JvqzDpIF@4T;V7)PYE;s z=5@=f`(C(i#}oUPj2?LU!adLQbv-rn)Yhq~t=rbG*Howm{%oT@N-o*0yik}YIuKty zr2?Z*p0j*I1{__!RG9S17RM_-BkMOr@hc>kN-4f$5k~P8-?3KnRlG;Km0#!Wxu0CL zhdU|VehIg=iuUhH2k5%uCnx`T52tTp8+iK&cEtPnPPstbmq9MZ^CWT99pv|2PuC$2 zevq(YCbp5%fX9z~E;)(z@k;S!P>y z$7=`U&4G?cseai;pZ|G>ZTybcZjU#Qtg$(mP5$SZOE3E3)`l-NMxC__8{`|^Mt}9* zTY(APdbBYxzTx^e@${$OKwj?4kW2oTD~`H+^2={HE3K;xR=wV)Uv2U^PyC7nwD(v{ zBRZ?mXw{7vt<0DGgboQcr=g44VD?a@5~b!e&ULvJlYqD)h#O*e`-C;8sUAiZ+50Kg z9Es-|U-Jp4&$LQkptsmp;ql)1RSI@0i*W=46jl*Qx_3?f}~IFi+Z_;iTkRJTX`fiUicsG?Ed3-Ers;K42!8SfZpl?x27iSc9_& zzjKpv{zgidFcV@Sg>{ zmIL*IN%sJSNw50=xq?9RR5DYAgvZf5X@M~2k_4J(UKSPt@GM^mU?cUpwTpCh!G%rb zcRb$+4@5Gh zPfPxRoIKffjxa6CbghmoJh!oFawrlRnrv!3cVXmMxHeE%7pM*E+M1VlRaJE@Z*E%F zQB~EktjQmX`9o0(62B2SwAHBJ>IFU?yx?vpG(s=)a)dnEg~~`7WR*7Ph2>P1j`jjY z*Q4B7()2?)O{fFJ-%MlbLy?3LKL3Ii-jgFW=X+BTJUGit<4r43?1ZX3jj#Snjw;-K zQQVqVfZ3^!v`6Fzbx$E~>U()nL&f8lLb!yl41YKqBa!kbbE{y&377Th?5|dlhaXCgXhC6W#1^S ze9qsyswtRkk2#9Vza8jZ)fnt=i|H;bsqNdgDC8~omf_1t7+*j#uDwUI3Oa%btN7-m z6LBPx*<*IwZMdT1uagnR2`1$8O{%v7Tp%`|N`8g^=odfY`JoWuznE%hL}3bt5&|Y} z*b1)!ln%Tiz7T8U#pnYuW?ZF1NVq^dplVF?O_N}6z2c7f_gzuY)F2OX2!unAn_VS7 zFM%MGIDTJQ$*~ZC)k^|qBiX{B+xn$H@-x*h)llun!UC@FX!w~*5^&JT;C?`5WAs4+ zp$;BWuIS*@(i6ruhhA2}3TjW=7z4wD%YZGf8pT^?cfe+be*w6!Nq|1Tk)Ljag&v?g z(-#W2?^C+z^eBu~2i`#_8uIv%JWBE37`HSHoeV*M)4+3M45_ZFYL9@C0jTAqp3Es5 zhLaXDqt4kBn?SJXht(rjo)cPIYh=aY@_@(iu)VUbOl!M3)IR-+reo&k3wKXM9qt=Y z;n`DDESKzZ1gjvXFzI3Td(C_Dt2pe>9u?e)2x0|2dkc z*dzL8R9Zr8E%bC?T9E^=t(~Ww0P%xDUAQyb@L(i-$wixySYfb~6+22Q%*7tfQ+?O> zUiiJ0T}GGL;x<_Q4V}KgiiW~MXStD{Zd7xKeN}H4)WH*$dhTu18@Q*u4J=71`P`?# zR?H=@xA*Pqz3{sk^6{<=c~DRNkbRYn{khcH*lDQC5=KVj;3LyYnc==NSXxdqI&kct z^{_oU_f=0fl0&B%WSPcTW8h=VpR>#->G7QNjx!266C&!_O@LfI_eoDTDMP0nQOuXd zz~}3K=SfdD!KZstikbRr?Ue4{rCJtE7Su)pHI)^F(X7OX(+OVhfGW=5uGdJRY6R9& zggB!zhyv4=_ITL+v{ZGKc0N)gVspC8X|0F>F42iji@G_T_$CYSE;y^VI59~S z#{?b)df7rJo7F*$q_!kW&D}wEQM`~+Cxs=+f z`BX_f-~l+af&H5c|4VrdD1+hnlOel-097t*|No^k0@ovnWMnPd@Sftu@U1O6JI zEb=;wcTe-af(W`BZvJvhz2nJLv7HT!#jAHyL92)%~MFYlyATf)K z>DcJrGt-#UUC}RHf(p%R_nE0+o{Sn8*f7yqPs7q$=1_hTkax+UK#@x52$2BOmGgoi z8W6Ne?>tAPqtjwgOnPNdR601;DPK`czd{czVKR^%iR?0n`&9-mi!Rl>;VeOxJw?9G z!xla@JSwa*i0U?EM^al)$0_WH!`~jPNDeLXD@+siFB^%0$*7@0|V@pYc zV69X!@e%u_V66reH)DuCx){+%EjhYOJ?4yO-O|v(kjI2BgLQx|v_!-A23 z+WpouPk*fzE?`wR#r^ZqP>rxlNRUOaEnY+-KWw@GHb4IW%LdpU7Q&uOc%Be?~3 z4LNpwZaU#Q%9&aQa|{Hzl;mG`ZMtD-?1uF%E$eR>8@p+J%WoVgYf>C3FDxvN0A{(; zAs@F#!ykV9v^`e+OWn(7Y_56xj7_u?69+c7v}`;uvGTS}t*x7GTbUS$6c$DX5{=1F zVPPoAwm2$IUh67ht0!)uhPR{}Hj9SMO2fu8{EmqM4y|nBIJA)0WjD#|`7mhbz|GEs zE(?l4KtgdqLO=pV7ij?b(T~_%oTGU_jj%VJnuOiu4Op}+QY@*51y(Do);B9QBQ<#n zE>TLifOQImv}8Ij+S6z!d$KW0&Pl_MpgHicwO3>9FJfKE%0jsx z6pco3cR(_mon~NS3vGSn{50{5q+o)|ywr_mGj<#RODL4-gZv;{&W0*R%ccV&1Qs0j zaixp|P(%$@3thvJth__syC4&SWVHmZ?2!e$Z4igCPw!5bm#9P;gyI#`}vm-OZRW6>#3_KDRC6I zEC+`^`1DoJ-?`Rx>1MV?^Yoq<4=%ZM{T{2UGFW-b{wZ6j$5v2Y;OAdHbC;vIve;p? zTyyD_%kF+Q~A_GvfW=yvUeB}cU^Dj_Gp{Ay`rZy6Wu^-+!RAhEQKQ*TDgIkNe z1vZ4p>R_F~e}L5pK(mo`fK$XKO=-R`J7B*>W0YbrqVypEhWIpQ;kJ?$LXZ>az>Jblgn`-N zZ*Avx9VB>ayucBp!`K_d7yQ$zdCpZ}aGJL|N=lDETd>a@!Wf3kS6iPwZgbgf#?+4w zzxi41Gj(;II?qqtmYFY3Uz+-z$LsYlRWY2w#E6%EwG2Y4DzkO;=bt5v)qq1@6hi7&(C4wE(EFJg5XLEs_Qe zs7e9vh{oNuZ8Z9|2OfMVI=ZbZB_|fJos+k(i`OsT^ZBRFKmV!E?^#|SpE@|nb}mRD z5-K)v<;49@JaIohV#b*dI%CInmu|iFfyt$ZZn|i^zJC0on+`3VeBjotrMr*CI*Dr$ zKcJn|eH#X-9_fLkJ6hk?Qr{Ep5fw#Awo7N7cf&R%l{3BEh)oGfrDDwl4Pqg&FJaV3 z26dpMu8u@}+N7Ke0!DIRp5B-msD0EsO$U7$6`p|MOC>_Il$da#xFN%QwtJL{LXbzE z@{2_h1DvQl%Y~WiR&U%ru<6082bUiC^QO_Eocx&q+EiF@ZZcI|_M9g27iU{#K*@ zrdr8~jlgxh4Sk6GT;}8dAXS4BwQNvm0fcQ$F!C zxE=$5Cs)BmsXhLd2-h9n#z9HR z(CKg0bsSE;kUElj0rhG=KFM^a=>H$%Ehq4ng?LM+ba#@6z0kgw_G;7=@F*CJJzj%u zz_C=+vMN-KTQve#rCY+HKn4Y>f}(a|F#acN(=9N%m`~ayDH2#r7?1)5`i8@nZxHLBrvv|L~a}&&?Pmma&2>&^hC09 zK~HUjNsEShmMvJ8Xo_^#c2`w+T&N}=W?>%PL>8v55}bgeG5D#me;`{0iU&3kktA={ zo3Pc;3Fqfuwe$v*H;) zT9BIPQ|wbHZYA@iO#ukX4?}h9Dv*Ob%}Uv!DrF!3R5vhSz-s7okDnt$rj+pDK5y;2YJe9hfoDZKBA~zicdi3(pAJ8u(jrnshd~B z1yaEDwh~uEPspvipsWySZ@-F8or z;f1mwe(~cMukm;V{o=2nee62vKCM}MNOCbtvWQA{b9m!SfTOUa61pN82_zQ5fa>(M z7!{~3kT8%}L+JJ#x(Z*&YKQ{f0=$a+d4!nQl9b=cLO0InO%70*eW2q}OFTei!`}e%}{`>FULtMOHn$SF|{dcT559z^F z=nZ;svdU2iCek5*l=zY!fCe8tLmRy$ViLHQ2Gs!&T!ISoK=7bA$^DwT(opK71w(=U z3T6wHO`p)z;s3kY^>27<9annnK&`&UQR{t!-I(gc$s|kfqTYl)-xC0<6gn@Z#L~a~ ztl>vC{t!>VJlvrb#2FI`2Pv2c(z8#ipFU9*Vz!EY|DvwcN1;;w0Ck$(*Q9QwXSl}V zGTJ>?(ldO#jX%NGQ_pGx@Tuw~H|o@2x!@Qiy_~8;KrJ*=z&YMKOK7l>M3k(_{w@DF zaXpqLOw6xwVVtO*uqH$@+g5hPTkML|U2k3eoXur4ItyNUsl;w{TjlpOf$4Xr-_-9R*GGQ9fKf& z@PkCQUl&YC7iSJUiq!a-3xR9@=Ps{%? zMI6?i0j}q=tEso35SSJQv)v%&rdrexr*qYV}t(sL;VMTawxXRZ34;@W4{ZPy#g1gb zw5#k(#vxc0s&FGnSHTaw{<31*(|>THXqh$jCPLjUu7a0kgT?eVuor)<`;GjPp@Q4t zQHxu(v~#(I`0RgLXOHBL9HZ}?GJ9qe+VoYn*OipiwO3McJ$AgLuCuzjldgzQ{efyg zU3zQVs;fj9&#IiiY4t{ck+VzxJZ9$XV=*G~;LLOS=K*2Bq#N~ijUJk6@%xC8>t+OQ8-a8WCh#w}0iVD_DXARQ$W7pw9WL|6(0_yV)3%^%{*{jv3AWI_vqS<~$#A6mW}5 zdLX)$pH4YE3B*+TP#zG2f(N8$fwszZJUR!nn(+rrhghm6FoSPceLjG}iRVkbqW=ib zhr}vWomHOikGem|M=*X4X(Po{+N@?HVk#ZX!DA}=T>tD9!ihI6oHz-LOdB&ysI7np6AlOob{iQqMpqf>mJk#gTjuNCP zn5VEgd)ZU*P(|B#Q*cpVi_25?aAn(gBYoGuSX9wI+3!cW(~@$0380K5F?Rra#7_P1 zAc(r8^OYz;m)SWhN|5vBUn~}oo*t*G5(=nnJuJa~{u(ME9lFll*&vUnPL?(Lk3C+} X)1d#|GoGS{N^72R6+cvrokIHmv=>@H literal 0 HcmV?d00001 diff --git a/libs/cmp-mifos-passcode/src/commonMain/composeResources/font/lato_bold.ttf b/libs/cmp-mifos-passcode/src/commonMain/composeResources/font/lato_bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..016068b486e1bd1d73743185c2752b7b1b843701 GIT binary patch literal 73332 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 + + Biometric + Passcode + hasPasscode + hasDragPasscode + passcode + drag_passcode + Create Passcode + Confirm Passcode + Enter your Passcode + Forgot Passcode + Delete Passcode Key Button + Drag your finger here only in one direction. + Drag your Pattern + Exit + Cancel + Skip + Forgot Passcode, Login Manually + Try again + Passcode do not match! + Are you sure you want to exit? + Use TouchId + Use FaceIdh + Authentication failed + Authentication not set + Feature unavailable + Biometric Registration Successful ! + Do you want to enable app lock ? + Use your existing PIN, pattern, face ID, or fingerprint to unlock this app. + Yes + No + Ok + \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/CipherUtilImpl.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/CipherUtilImpl.kt new file mode 100644 index 000000000..c5bdaec31 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/CipherUtilImpl.kt @@ -0,0 +1,18 @@ +package com.mifos.passcode + +interface ICipherUtil { + @Throws(Exception::class) + fun generateKeyPair(): com.mifos.passcode.CommonKeyPair + + fun getPublicKey(): com.mifos.passcode.CommonPublicKey? + + @Throws(Exception::class) + fun getCrypto(): com.mifos.passcode.Crypto + + @Throws(Exception::class) + suspend fun removePublicKey() +} + +expect class CommonKeyPair +expect interface CommonPublicKey +expect class Crypto \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/Greeting.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/Greeting.kt new file mode 100644 index 000000000..c8ce7120e --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/Greeting.kt @@ -0,0 +1,11 @@ +package com.mifos.passcode + +import com.mifos.passcode.Platform + +class Greeting { + private val platform: com.mifos.passcode.Platform = com.mifos.passcode.getPlatform() + + fun greet(): String { + return "Hello, ${platform.name}!" + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/PasscodeNavigation.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/PasscodeNavigation.kt new file mode 100644 index 000000000..4be56d5b9 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/PasscodeNavigation.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2024 Mifos Initiative + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + * + * See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md + */ +package com.mifos.passcode + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions +import androidx.navigation.compose.composable +import com.mifos.passcode.component.PasscodeScreen +import com.mifos.passcode.utility.BioMetricUtil + +const val PASSCODE_SCREEN = "passcode_screen" + +fun NavGraphBuilder.passcodeRoute( + onForgotButton: () -> Unit, + onSkipButton: () -> Unit, + onPasscodeConfirm: (String) -> Unit, + onPasscodeRejected: () -> Unit, + enableBiometric: Boolean, + bioMetricUtil: BioMetricUtil, + onBiometricAuthSucess: () -> Unit, +) { + composable( + route = PASSCODE_SCREEN, + ) { + PasscodeScreen( + onForgotButton = onForgotButton, + onSkipButton = onSkipButton, + onPasscodeConfirm = onPasscodeConfirm, + onPasscodeRejected = onPasscodeRejected, + enableBiometric = enableBiometric, + bioMetricUtil = bioMetricUtil, + onBiometricAuthSuccess = onBiometricAuthSucess, + ) + } +} + +fun NavController.navigateToPasscodeScreen(options: NavOptions? = null) { + navigate(PASSCODE_SCREEN, options) +} diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/Platform.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/Platform.kt new file mode 100644 index 000000000..c6af4a74a --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/Platform.kt @@ -0,0 +1,7 @@ +package com.mifos.passcode + +interface Platform { + val name: String +} + +expect fun getPlatform(): Platform \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/BackSpace.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/BackSpace.kt new file mode 100644 index 000000000..5cf67a44f --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/BackSpace.kt @@ -0,0 +1,45 @@ +package com.mifos.passcode.component + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.materialIcon +import androidx.compose.material.icons.materialPath +import androidx.compose.ui.graphics.vector.ImageVector + +public val Icons.Filled.Backspace: ImageVector + get() { + if (com.mifos.passcode.component._backspace != null) { + return com.mifos.passcode.component._backspace!! + } + com.mifos.passcode.component._backspace = materialIcon(name = "Filled.Backspace") { + materialPath { + moveTo(22.0f, 3.0f) + lineTo(7.0f, 3.0f) + curveToRelative(-0.69f, 0.0f, -1.23f, 0.35f, -1.59f, 0.88f) + lineTo(0.0f, 12.0f) + lineToRelative(5.41f, 8.11f) + curveToRelative(0.36f, 0.53f, 0.9f, 0.89f, 1.59f, 0.89f) + horizontalLineToRelative(15.0f) + curveToRelative(1.1f, 0.0f, 2.0f, -0.9f, 2.0f, -2.0f) + lineTo(24.0f, 5.0f) + curveToRelative(0.0f, -1.1f, -0.9f, -2.0f, -2.0f, -2.0f) + close() + moveTo(19.0f, 15.59f) + lineTo(17.59f, 17.0f) + lineTo(14.0f, 13.41f) + lineTo(10.41f, 17.0f) + lineTo(9.0f, 15.59f) + lineTo(12.59f, 12.0f) + lineTo(9.0f, 8.41f) + lineTo(10.41f, 7.0f) + lineTo(14.0f, 10.59f) + lineTo(17.59f, 7.0f) + lineTo(19.0f, 8.41f) + lineTo(15.41f, 12.0f) + lineTo(19.0f, 15.59f) + close() + } + } + return com.mifos.passcode.component._backspace!! + } + +private var _backspace: ImageVector? = null diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/MifosIcon.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/MifosIcon.kt new file mode 100644 index 000000000..8926e9da9 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/MifosIcon.kt @@ -0,0 +1,26 @@ +package com.mifos.passcode.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.mifos.passcode.resources.Res +import com.mifos.passcode.resources.mifos_logo +import org.jetbrains.compose.resources.painterResource + +@Composable +fun MifosIcon(modifier: Modifier) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.Center + ) { + Image( + modifier = Modifier.size(180.dp), + painter = painterResource(resource= Res.drawable.mifos_logo), + contentDescription = null + ) + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PassCodeScreen.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PassCodeScreen.kt new file mode 100644 index 000000000..ff579754d --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PassCodeScreen.kt @@ -0,0 +1,309 @@ +package com.mifos.passcode.component + +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.Animatable +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.mifos.passcode.resources.Res +import com.mifos.passcode.resources.biometric_registration_success +import com.mifos.passcode.resources.ok +import com.mifos.passcode.theme.blueTint +import com.mifos.passcode.utility.BioMetricUtil +import com.mifos.passcode.utility.Constants.PASSCODE_LENGTH +import com.mifos.passcode.utility.PreferenceManager +import com.mifos.passcode.utility.ShakeAnimation.performShakeAnimation +import com.mifos.passcode.viewmodels.BiometricAuthorizationViewModel +import com.mifos.passcode.viewmodels.BiometricEffect +import com.mifos.passcode.viewmodels.PasscodeViewModel +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import org.jetbrains.compose.resources.getString + +/** + * @author pratyush + * @since 15/3/24 + */ + +@Composable +fun PasscodeScreen( + viewModel: PasscodeViewModel = viewModel { PasscodeViewModel() }, + onForgotButton: () -> Unit, + onSkipButton: () -> Unit, + onPasscodeConfirm: (String) -> Unit, + onPasscodeRejected: () -> Unit, + enableBiometric: Boolean = false, + onBiometricAuthSuccess: () -> Unit = {}, + biometricAuthorizationViewModel: BiometricAuthorizationViewModel = viewModel(), + bioMetricUtil: BioMetricUtil? = null, +) { + val preferenceManager = remember { PreferenceManager() } + val activeStep by viewModel.activeStep.collectAsState() + val filledDots by viewModel.filledDots.collectAsState() + val passcodeVisible by viewModel.passcodeVisible.collectAsState() + val currentPasscode by viewModel.currentPasscodeInput.collectAsState() + val xShake = remember { Animatable(initialValue = 0.0F) } + var passcodeRejectedDialogVisible by remember { mutableStateOf(false) } + val biometricState by biometricAuthorizationViewModel.state.collectAsState() + var biometricMessage by rememberSaveable { mutableStateOf("") } + val coroutineScope = rememberCoroutineScope() + var showBiometricDialog by rememberSaveable{ mutableStateOf(false) } + + + if(showBiometricDialog) + { + PasscodeBiometricConfirmDialog( + setBiometric = { + biometricAuthorizationViewModel.setBiometricAuthorization(bioMetricUtil!!) + }, + cancelBiometric = { + showBiometricDialog = false + } + ) + } + + if(enableBiometric) { + biometricState.error?.let { + biometricMessage = it + } + } + + LaunchedEffect(key1 = Unit) { + if(enableBiometric) { + biometricAuthorizationViewModel.effect.collectLatest { + when (it) { + BiometricEffect.BiometricAuthSuccess -> { + onBiometricAuthSuccess.invoke() + showBiometricDialog = false + } + + BiometricEffect.BiometricSetSuccess -> { + biometricMessage = getString(Res.string.biometric_registration_success) + showBiometricDialog = false + } + } + } + } + } + + LaunchedEffect(key1 = viewModel.onPasscodeConfirmed) { + viewModel.onPasscodeConfirmed.collect { + onPasscodeConfirm(it) + } + } + LaunchedEffect(key1 = viewModel.onPasscodeRejected) { + viewModel.onPasscodeRejected.collect { + passcodeRejectedDialogVisible = true +// vibrateFeedback(context) + performShakeAnimation(xShake) + onPasscodeRejected() + } + } + + LaunchedEffect(true) { + if(preferenceManager.hasPasscode && enableBiometric) { + if(bioMetricUtil!!.isBiometricSet()) + biometricAuthorizationViewModel.authorizeBiometric(bioMetricUtil) + else + showBiometricDialog = true + } + } + + val snackBarHostState = remember { + SnackbarHostState() + } + + Scaffold( + snackbarHost = { SnackbarHost(snackBarHostState) } + ) { + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + PasscodeToolbar(activeStep = activeStep, preferenceManager.hasPasscode) + PasscodeSkipButton( + onSkipButton = { onSkipButton.invoke() }, + hasPassCode = preferenceManager.hasPasscode + ) + MifosIcon(modifier = Modifier.fillMaxWidth()) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp, bottom = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + PasscodeHeader( + activeStep = activeStep, + isPasscodeAlreadySet = preferenceManager.hasPasscode + ) + PasscodeView( + filledDots = filledDots, + currentPasscode = currentPasscode, + passcodeVisible = passcodeVisible, + togglePasscodeVisibility = { viewModel.togglePasscodeVisibility() }, + restart = { viewModel.restart() }, + passcodeRejectedDialogVisible = passcodeRejectedDialogVisible, + onDismissDialog = { passcodeRejectedDialogVisible = false }, + xShake = xShake + ) + } + Spacer(modifier = Modifier.height(6.dp)) + PasscodeKeys( + enterKey = { viewModel.enterKey(it) }, + deleteKey = { viewModel.deleteKey() }, + deleteAllKeys = { viewModel.deleteAllKeys() }, + modifier = Modifier.padding(horizontal = 12.dp) + ) + Spacer(modifier = Modifier.height(8.dp)) + PasscodeForgotButton( + onForgotButton = { onForgotButton.invoke() }, + hasPassCode = preferenceManager.hasPasscode + ) + + UseTouchIdButton( + onClick = { + if ( bioMetricUtil!!.isBiometricSet() ) + biometricAuthorizationViewModel.authorizeBiometric(bioMetricUtil) + else + showBiometricDialog = true + }, + hasPassCode = preferenceManager.hasPasscode, + enableBiometric = enableBiometric + ) + + LaunchedEffect ( biometricMessage ) { + if(biometricMessage.isNotEmpty()) { + coroutineScope.launch { + snackBarHostState.showSnackbar( + message = biometricMessage, + duration = SnackbarDuration.Short, + withDismissAction = false, + actionLabel = getString(Res.string.ok) + ) + } + } + } + } + } +} + +@Composable +private fun PasscodeView( + modifier: Modifier = Modifier, + restart: () -> Unit, + togglePasscodeVisibility: () -> Unit, + filledDots: Int, + passcodeVisible: Boolean, + currentPasscode: String, + passcodeRejectedDialogVisible: Boolean, + onDismissDialog: () -> Unit, + xShake: Animatable +) { + PasscodeMismatchedDialog( + visible = passcodeRejectedDialogVisible, + onDismiss = { + onDismissDialog.invoke() + restart() + } + ) + + Row( + modifier = modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + modifier = modifier.offset(x = xShake.value.dp), + horizontalArrangement = Arrangement.spacedBy( + space = 26.dp, + alignment = Alignment.CenterHorizontally + ), + verticalAlignment = Alignment.CenterVertically + ) { + repeat(PASSCODE_LENGTH) { dotIndex -> + if (passcodeVisible && dotIndex < currentPasscode.length) { + Text( + text = currentPasscode[dotIndex].toString(), + color = blueTint + ) + } else { + val isFilledDot = dotIndex + 1 <= filledDots + val dotColor = animateColorAsState( + if (isFilledDot) blueTint else Color.Gray, label = "" + ) + + Box( + modifier = Modifier + .size(14.dp) + .background( + color = dotColor.value, + shape = CircleShape + ) + ) + } + } + } + IconButton( + onClick = { togglePasscodeVisibility.invoke() }, + modifier = Modifier.padding(start = 10.dp) + ) { + Icon( + imageVector = if (passcodeVisible) Icons.Filled.Visibility else Icons.Filled.VisibilityOff, + contentDescription = null + ) + } + } +} + +//@Preview(showBackground = true) +//@Composable +//fun PasscodeScreenPreview() { +// PasscodeScreen( +// viewModel = PasscodeViewModel(object : PasscodeRepository { +// override fun getSavedPasscode(): String { +// return "" +// } +// +// override val hasPasscode: Boolean +// get() = false +// +// override fun savePasscode(passcode: String) {} +// +// }), +// {}, {}, {}, {} +// ) +//} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeBiometricConfirmDialog.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeBiometricConfirmDialog.kt new file mode 100644 index 000000000..4875a8ec1 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeBiometricConfirmDialog.kt @@ -0,0 +1,116 @@ +package com.mifos.passcode.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.heightIn +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Color.Companion.White +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import com.mifos.passcode.resources.Res +import com.mifos.passcode.resources.enable_biometric_dialog_description +import com.mifos.passcode.resources.enable_biometric_dialog_title +import com.mifos.passcode.resources.no +import com.mifos.passcode.resources.yes +import com.mifos.passcode.theme.blueTint +import org.jetbrains.compose.resources.stringResource + +@Composable +fun PasscodeBiometricConfirmDialog( + cancelBiometric: () -> Unit, + setBiometric: () -> Unit +) { + + Dialog(onDismissRequest = { cancelBiometric.invoke() }) { + + Box( + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .background(Color.White) + .padding(16.dp) + ) { + + Column { + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = stringResource(resource = Res.string.enable_biometric_dialog_title), + modifier = Modifier + .padding(8.dp), + fontSize = 20.sp + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = stringResource(resource = Res.string.enable_biometric_dialog_description), + modifier = Modifier + .padding(8.dp), + fontSize = 12.sp + ) + + Spacer(modifier = Modifier.height(12.dp)) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp) + ) { + DialogButton( + onClick = { cancelBiometric.invoke() }, + modifier = Modifier + .padding(end = 8.dp) + .weight(1f), + text = stringResource(resource = Res.string.no) + ) + + DialogButton( + onClick = { setBiometric.invoke() }, + modifier = Modifier + .padding(start = 8.dp) + .weight(1f), + text = stringResource(resource = Res.string.yes) + ) + } + } + } + } +} + +@Composable +fun DialogButton( + text: String, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Button( + onClick = onClick, + modifier = modifier + .fillMaxWidth() + .height(36.dp), + colors = ButtonDefaults.buttonColors( + containerColor = blueTint, + contentColor = White, + disabledContainerColor = Color.DarkGray, + disabledContentColor = White + ) + ) { + Text(text = text) + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeButton.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeButton.kt new file mode 100644 index 000000000..c799d72f4 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeButton.kt @@ -0,0 +1,91 @@ +package com.mifos.passcode.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.mifos.passcode.resources.Res +import com.mifos.passcode.resources.forgot_passcode_login_manually +import com.mifos.passcode.resources.skip +import com.mifos.passcode.resources.use_faceId +import com.mifos.passcode.resources.use_touchId +import com.mifos.passcode.theme.forgotButtonStyle +import com.mifos.passcode.theme.skipButtonStyle +import com.mifos.passcode.theme.useTouchIdButtonStyle +import org.jetbrains.compose.resources.stringResource + +@Composable +fun PasscodeSkipButton( + onSkipButton: () -> Unit, + hasPassCode: Boolean +) { + if (!hasPassCode) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(end = 16.dp), + horizontalArrangement = Arrangement.End + ) { + TextButton( + onClick = { onSkipButton.invoke() } + ) { + Text(text = stringResource(Res.string.skip), style = skipButtonStyle()) + } + } + } + +} + +@Composable +fun PasscodeForgotButton( + onForgotButton: () -> Unit, + hasPassCode: Boolean +) { + if (hasPassCode) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(end = 16.dp), + horizontalArrangement = Arrangement.Center + ) { + TextButton( + onClick = { onForgotButton.invoke() } + ) { + Text( + text = stringResource(Res.string.forgot_passcode_login_manually), + style = forgotButtonStyle() + ) + } + } + } +} + +@Composable +fun UseTouchIdButton( + onClick: () -> Unit, + hasPassCode: Boolean, + enableBiometric: Boolean +) { + if (hasPassCode && enableBiometric) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(end = 16.dp), + horizontalArrangement = Arrangement.Center + ) { + TextButton( + onClick = onClick + ) { + if(com.mifos.passcode.getPlatform().name == "Android") + Text(text = stringResource(Res.string.use_touchId), style = useTouchIdButtonStyle()) + else + Text(text = stringResource(Res.string.use_faceId), style = useTouchIdButtonStyle()) + } + } + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeHeader.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeHeader.kt new file mode 100644 index 000000000..d676ca027 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeHeader.kt @@ -0,0 +1,114 @@ +package com.mifos.passcode.component + +import androidx.compose.animation.core.MutableTransitionState +import androidx.compose.animation.core.Transition +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.animateOffset +import androidx.compose.animation.core.updateTransition +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.scale +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.mifos.passcode.resources.Res +import com.mifos.passcode.resources.confirm_passcode +import com.mifos.passcode.resources.create_passcode +import com.mifos.passcode.resources.enter_your_passcode +import com.mifos.passcode.utility.Step +import org.jetbrains.compose.resources.stringResource + +@Composable +fun PasscodeHeader( + modifier: Modifier = Modifier, + activeStep: Step, + isPasscodeAlreadySet: Boolean, +) { + val transitionState = remember { MutableTransitionState(activeStep) } + transitionState.targetState = activeStep + + val transition: Transition = updateTransition( + transitionState = transitionState, + label = "Headers Transition" + ) + + val offset = 200.0F + val zeroOffset = Offset(x = 0.0F, y = 0.0F) + val negativeOffset = Offset(x = -offset, y = 0.0F) + val positiveOffset = Offset(x = offset, y = 0.0F) + + val xTransitionHeader1 by transition.animateOffset(label = "Transition Offset Header 1") { + if (it == Step.Create) zeroOffset else negativeOffset + } + val xTransitionHeader2 by transition.animateOffset(label = "Transition Offset Header 2") { + if (it == Step.Confirm) zeroOffset else positiveOffset + } + val alphaHeader1 by transition.animateFloat(label = "Transition Alpha Header 1") { + if (it == Step.Create) 1.0F else 0.0F + } + val alphaHeader2 by transition.animateFloat(label = "Transition Alpha Header 2") { + if (it == Step.Confirm) 1.0F else 0.0F + } + val scaleHeader1 by transition.animateFloat(label = "Transition Alpha Header 1") { + if (it == Step.Create) 1.0F else 0.5F + } + val scaleHeader2 by transition.animateFloat(label = "Transition Alpha Header 2") { + if (it == Step.Confirm) 1.0F else 0.5F + } + + Box( + modifier = modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Box( + modifier = modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + if (isPasscodeAlreadySet) { + Text( + modifier = Modifier + .offset(x = xTransitionHeader1.x.dp) + .alpha(alpha = alphaHeader1) + .scale(scale = scaleHeader1), + text = stringResource(resource = Res.string.enter_your_passcode), + style = TextStyle(fontSize = 20.sp) + ) + } else { + if (activeStep == Step.Create) { + Text( + modifier = Modifier + .offset(x = xTransitionHeader1.x.dp) + .alpha(alpha = alphaHeader1) + .scale(scale = scaleHeader1), + text = stringResource(resource = Res.string.create_passcode), + style = TextStyle(fontSize = 20.sp) + ) + } else if (activeStep == Step.Confirm) { + Text( + modifier = Modifier + .offset(x = xTransitionHeader2.x.dp) + .alpha(alpha = alphaHeader2) + .scale(scale = scaleHeader2), + text = stringResource(resource = Res.string.confirm_passcode), + style = TextStyle(fontSize = 20.sp) + ) + } + } + } + } +} + +//@Preview +//@Composable +//fun PasscodeHeaderPreview() { +// PasscodeHeader(activeStep = Step.Create, isPasscodeAlreadySet = true) +//} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeKeys.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeKeys.kt new file mode 100644 index 000000000..d2c9bb03a --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeKeys.kt @@ -0,0 +1,199 @@ +package com.mifos.passcode.component + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.Image +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.mifos.passcode.theme.PasscodeKeyButtonStyle +import com.mifos.passcode.theme.blueTint + +@Composable +fun PasscodeKeys( + enterKey: (String) -> Unit, + deleteKey: () -> Unit, + deleteAllKeys: () -> Unit, + modifier: Modifier = Modifier, +) { + val onEnterKeyClick = { keyTitle: String -> + enterKey(keyTitle) + } + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Row(modifier = Modifier.fillMaxWidth()) { + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyTitle = "1", + onClick = onEnterKeyClick + ) + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyTitle = "2", + onClick = onEnterKeyClick + ) + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyTitle = "3", + onClick = onEnterKeyClick + ) + } + Row(modifier = Modifier.fillMaxWidth()) { + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyTitle = "4", + onClick = onEnterKeyClick + ) + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyTitle = "5", + onClick = onEnterKeyClick + ) + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyTitle = "6", + onClick = onEnterKeyClick + ) + } + Row(modifier = Modifier.fillMaxWidth()) { + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyTitle = "7", + onClick = onEnterKeyClick + ) + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyTitle = "8", + onClick = onEnterKeyClick + ) + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyTitle = "9", + onClick = onEnterKeyClick + ) + } + Row(modifier = Modifier.fillMaxWidth()) { + PasscodeKey(modifier = Modifier.weight(weight = 1.0F)) + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyTitle = "0", + onClick = onEnterKeyClick + ) + PasscodeKey( + modifier = Modifier.weight(weight = 1.0F), + keyIcon = Icons.Filled.Backspace, + keyIconContentDescription = "Delete Passcode Key Button", + onClick = { + deleteKey() + }, + onLongClick = { + deleteAllKeys() + } + ) + } + } +} + +@Composable +fun PasscodeKey( + modifier: Modifier = Modifier, + keyTitle: String = "", + keyIcon: ImageVector? = null, + keyIconContentDescription: String = "", + onClick: ((String) -> Unit)? = null, + onLongClick: (() -> Unit)? = null +) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.Center + ) { + CombinedClickableIconButton( + modifier = Modifier + .padding(all = 4.dp), + onClick = { + onClick?.invoke(keyTitle) + }, + onLongClick = { + onLongClick?.invoke() + } + ) { + if (keyIcon == null) { + Text( + text = keyTitle, + style = PasscodeKeyButtonStyle().copy(color = blueTint) + ) + } else { + Icon( + imageVector = Icons.Default.Backspace, + contentDescription = keyIconContentDescription, + tint = blueTint + ) + } + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun CombinedClickableIconButton( + onClick: () -> Unit, + onLongClick: () -> Unit, + modifier: Modifier = Modifier, + size: Dp = 48.dp, + rippleRadius: Dp = 36.dp, + enabled: Boolean = true, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, + content: @Composable () -> Unit +) { + Column( + modifier = modifier + .size(size = size) + .combinedClickable( + onClick = onClick, + onLongClick = onLongClick, + enabled = enabled, + role = Role.Button, + interactionSource = interactionSource, + indication = rememberRipple( + bounded = false, + radius = rippleRadius, + color = Color.Cyan + ) + ), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + val contentAlpha = + if (enabled) LocalContentColor.current else LocalContentColor.current.copy(alpha = 0f) + CompositionLocalProvider(LocalContentColor provides contentAlpha, content = content) + } +} + + +//@Preview +//@Composable +//fun PasscodeKeysPreview() { +// PasscodeKeys({}, {}, {}) +//} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeMismatchedDialog.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeMismatchedDialog.kt new file mode 100644 index 000000000..a116d7043 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeMismatchedDialog.kt @@ -0,0 +1,37 @@ +package com.mifos.passcode.component + +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import com.mifos.passcode.resources.Res +import com.mifos.passcode.resources.passcode_do_not_match +import com.mifos.passcode.resources.try_again +import org.jetbrains.compose.resources.stringResource + +@Composable +fun PasscodeMismatchedDialog( + visible: Boolean, + onDismiss: () -> Unit +) { + if (visible) { + AlertDialog( + shape = MaterialTheme.shapes.large, + containerColor = Color.White, + title = { + Text( + text = stringResource(Res.string.passcode_do_not_match), + color = Color.Black + ) + }, + confirmButton = { + TextButton(onClick = onDismiss) { + Text(text = stringResource(Res.string.try_again), color = Color.Black) + } + }, + onDismissRequest = onDismiss + ) + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeStepIndicator.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeStepIndicator.kt new file mode 100644 index 000000000..9789c1466 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeStepIndicator.kt @@ -0,0 +1,50 @@ +package com.mifos.passcode.component + +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mifos.passcode.theme.blueTint +import com.mifos.passcode.utility.Constants.STEPS_COUNT +import com.mifos.passcode.utility.Step + +@Composable +fun PasscodeStepIndicator( + modifier: Modifier = Modifier, + activeStep: Step +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy( + space = 6.dp, + alignment = Alignment.CenterHorizontally + ) + ) { + repeat(STEPS_COUNT) { step -> + val isActiveStep = step <= activeStep.index + val stepColor = + animateColorAsState(if (isActiveStep) blueTint else Color.Gray, label = "") + + Box( + modifier = Modifier + .size( + width = 72.dp, + height = 4.dp + ) + .background( + color = stepColor.value, + shape = MaterialTheme.shapes.medium + ) + ) + } + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeToolbar.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeToolbar.kt new file mode 100644 index 000000000..17d09357d --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeToolbar.kt @@ -0,0 +1,80 @@ +package com.mifos.passcode.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.mifos.passcode.resources.Res +import com.mifos.passcode.resources.are_you_sure_you_want_to_exit +import com.mifos.passcode.resources.cancel +import com.mifos.passcode.resources.exit +import com.mifos.passcode.utility.Step +import org.jetbrains.compose.resources.stringResource + +@Composable +fun PasscodeToolbar(activeStep: Step, hasPasscode: Boolean) { + var exitWarningDialogVisible by remember { mutableStateOf(false) } + ExitWarningDialog( + visible = exitWarningDialogVisible, + onConfirm = {}, + onDismiss = { + exitWarningDialogVisible = false + } + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + horizontalArrangement = Arrangement.Center + ) { + if (!hasPasscode) { + PasscodeStepIndicator( + activeStep = activeStep + ) + } + } +} + +@Composable +fun ExitWarningDialog( + visible: Boolean, + onConfirm: () -> Unit, + onDismiss: () -> Unit +) { + if (visible) { + AlertDialog( + shape = MaterialTheme.shapes.large, + containerColor = Color.White, + title = { + Text( + text = stringResource(Res.string.are_you_sure_you_want_to_exit), + color = Color.Black + ) + }, + confirmButton = { + TextButton(onClick = onConfirm) { + Text(text = stringResource(Res.string.exit)) + } + }, + dismissButton = { + TextButton(onClick = onDismiss) { + Text(text = stringResource(Res.string.cancel)) + } + }, + onDismissRequest = onDismiss + ) + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/Visibility.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/Visibility.kt new file mode 100644 index 000000000..272999b3d --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/Visibility.kt @@ -0,0 +1,58 @@ +package com.mifos.passcode.component + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.materialIcon +import androidx.compose.material.icons.materialPath +import androidx.compose.ui.graphics.vector.ImageVector + +public val Icons.Filled.VisibilityOff: ImageVector + get() { + if (_visibilityOff != null) { + return _visibilityOff!! + } + _visibilityOff = materialIcon(name = "Filled.VisibilityOff") { + materialPath { + moveTo(12.0f, 7.0f) + curveToRelative(2.76f, 0.0f, 5.0f, 2.24f, 5.0f, 5.0f) + curveToRelative(0.0f, 0.65f, -0.13f, 1.26f, -0.36f, 1.83f) + lineToRelative(2.92f, 2.92f) + curveToRelative(1.51f, -1.26f, 2.7f, -2.89f, 3.43f, -4.75f) + curveToRelative(-1.73f, -4.39f, -6.0f, -7.5f, -11.0f, -7.5f) + curveToRelative(-1.4f, 0.0f, -2.74f, 0.25f, -3.98f, 0.7f) + lineToRelative(2.16f, 2.16f) + curveTo(10.74f, 7.13f, 11.35f, 7.0f, 12.0f, 7.0f) + close() + moveTo(2.0f, 4.27f) + lineToRelative(2.28f, 2.28f) + lineToRelative(0.46f, 0.46f) + curveTo(3.08f, 8.3f, 1.78f, 10.02f, 1.0f, 12.0f) + curveToRelative(1.73f, 4.39f, 6.0f, 7.5f, 11.0f, 7.5f) + curveToRelative(1.55f, 0.0f, 3.03f, -0.3f, 4.38f, -0.84f) + lineToRelative(0.42f, 0.42f) + lineTo(19.73f, 22.0f) + lineTo(21.0f, 20.73f) + lineTo(3.27f, 3.0f) + lineTo(2.0f, 4.27f) + close() + moveTo(7.53f, 9.8f) + lineToRelative(1.55f, 1.55f) + curveToRelative(-0.05f, 0.21f, -0.08f, 0.43f, -0.08f, 0.65f) + curveToRelative(0.0f, 1.66f, 1.34f, 3.0f, 3.0f, 3.0f) + curveToRelative(0.22f, 0.0f, 0.44f, -0.03f, 0.65f, -0.08f) + lineToRelative(1.55f, 1.55f) + curveToRelative(-0.67f, 0.33f, -1.41f, 0.53f, -2.2f, 0.53f) + curveToRelative(-2.76f, 0.0f, -5.0f, -2.24f, -5.0f, -5.0f) + curveToRelative(0.0f, -0.79f, 0.2f, -1.53f, 0.53f, -2.2f) + close() + moveTo(11.84f, 9.02f) + lineToRelative(3.15f, 3.15f) + lineToRelative(0.02f, -0.16f) + curveToRelative(0.0f, -1.66f, -1.34f, -3.0f, -3.0f, -3.0f) + lineToRelative(-0.17f, 0.01f) + close() + } + } + return _visibilityOff!! + } + +private var _visibilityOff: ImageVector? = null diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/VisibilityOff.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/VisibilityOff.kt new file mode 100644 index 000000000..d6a7704ed --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/VisibilityOff.kt @@ -0,0 +1,38 @@ +package com.mifos.passcode.component + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.materialIcon +import androidx.compose.material.icons.materialPath +import androidx.compose.ui.graphics.vector.ImageVector + +public val Icons.Filled.Visibility: ImageVector + get() { + if (_visibility != null) { + return _visibility!! + } + _visibility = materialIcon(name = "Filled.Visibility") { + materialPath { + moveTo(12.0f, 4.5f) + curveTo(7.0f, 4.5f, 2.73f, 7.61f, 1.0f, 12.0f) + curveToRelative(1.73f, 4.39f, 6.0f, 7.5f, 11.0f, 7.5f) + reflectiveCurveToRelative(9.27f, -3.11f, 11.0f, -7.5f) + curveToRelative(-1.73f, -4.39f, -6.0f, -7.5f, -11.0f, -7.5f) + close() + moveTo(12.0f, 17.0f) + curveToRelative(-2.76f, 0.0f, -5.0f, -2.24f, -5.0f, -5.0f) + reflectiveCurveToRelative(2.24f, -5.0f, 5.0f, -5.0f) + reflectiveCurveToRelative(5.0f, 2.24f, 5.0f, 5.0f) + reflectiveCurveToRelative(-2.24f, 5.0f, -5.0f, 5.0f) + close() + moveTo(12.0f, 9.0f) + curveToRelative(-1.66f, 0.0f, -3.0f, 1.34f, -3.0f, 3.0f) + reflectiveCurveToRelative(1.34f, 3.0f, 3.0f, 3.0f) + reflectiveCurveToRelative(3.0f, -1.34f, 3.0f, -3.0f) + reflectiveCurveToRelative(-1.34f, -3.0f, -3.0f, -3.0f) + close() + } + } + return _visibility!! + } + +private var _visibility: ImageVector? = null \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/PasscodeRepository.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/PasscodeRepository.kt new file mode 100644 index 000000000..ab7ebc5b7 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/PasscodeRepository.kt @@ -0,0 +1,8 @@ +package com.mifos.passcode.data + +interface PasscodeRepository { + fun getSavedPasscode(): String + val hasPasscode: Boolean + fun savePasscode(passcode: String) + fun clearPasscode() +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/PasscodeRepositoryImpl.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/PasscodeRepositoryImpl.kt new file mode 100644 index 000000000..d16945592 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/PasscodeRepositoryImpl.kt @@ -0,0 +1,23 @@ +package com.mifos.passcode.data + +import com.mifos.passcode.utility.PreferenceManager + + +class PasscodeRepositoryImpl constructor(private val preferenceManager: PreferenceManager) : + PasscodeRepository { + + override fun getSavedPasscode(): String { + return preferenceManager.getSavedPasscode() + } + + override val hasPasscode: Boolean + get() = preferenceManager.hasPasscode + + override fun savePasscode(passcode: String) { + preferenceManager.savePasscode(passcode) + } + + override fun clearPasscode() { + preferenceManager.clearPasscode() + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/SetBiometricPublicKeyRepository.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/SetBiometricPublicKeyRepository.kt new file mode 100644 index 000000000..0c9c39690 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/data/SetBiometricPublicKeyRepository.kt @@ -0,0 +1,17 @@ +package com.mifos.passcode.data + +import kotlinx.coroutines.delay + +private var publicKeyOnServer = "" +class SetBiometricPublicKeyRepository { + suspend fun set(publicKey: String) { + delay(500) + publicKeyOnServer = publicKey + } +} + +class VerifyBiometric { + suspend fun verify(signedUserId: String): Result { + return Result.success(Unit) + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Color.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Color.kt new file mode 100644 index 000000000..0c4d15f8f --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Color.kt @@ -0,0 +1,5 @@ +package com.mifos.passcode.theme + +import androidx.compose.ui.graphics.Color + +val blueTint = Color(0xFF03A9F4) \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Font.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Font.kt new file mode 100644 index 000000000..dae6c8fd7 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Font.kt @@ -0,0 +1,30 @@ +package com.mifos.passcode.theme + +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import com.mifos.passcode.resources.Res +import com.mifos.passcode.resources.lato_black +import com.mifos.passcode.resources.lato_bold +import com.mifos.passcode.resources.lato_regular +import org.jetbrains.compose.resources.Font + +@Composable +fun LatoFonts() = FontFamily( + Font( + resource = Res.font.lato_regular, + weight = FontWeight.Normal, + style = FontStyle.Normal + ), + Font( + resource = Res.font.lato_bold, + weight = FontWeight.Bold, + style = FontStyle.Normal + ), + Font( + resource = Res.font.lato_black, + weight = FontWeight.Black, + style = FontStyle.Normal + ) +) \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Theme.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Theme.kt new file mode 100644 index 000000000..92ce16c22 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Theme.kt @@ -0,0 +1,40 @@ +package com.mifos.passcode.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Color.Companion.Blue + +private val DarkColorPalette = darkColorScheme( + primary = Color.Cyan, + onPrimary = Color.Cyan, + secondary = Color.Black.copy(alpha = 0.2f), + background = Color.Black +) +private val LightColorPalette = lightColorScheme( + primary = Blue, + onPrimary = Blue, + secondary = Color.Blue.copy(alpha = 0.4f), + background = Color.White +) + +@Composable +fun MifosPasscodeTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + val colors = if (darkTheme) { + DarkColorPalette + } else { + LightColorPalette + } + + MaterialTheme( + colorScheme = colors, + typography = Typography(), + content = content + ) +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Type.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Type.kt new file mode 100644 index 000000000..1fe3a2e11 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/theme/Type.kt @@ -0,0 +1,57 @@ +package com.mifos.passcode.theme + +import androidx.compose.material3.Typography +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +@Composable +fun Typography() = Typography().run { + val fontFamily = LatoFonts() + copy( + displayLarge = displayLarge.copy(fontFamily = fontFamily), + displayMedium = displayMedium.copy(fontFamily = fontFamily), + displaySmall = displaySmall.copy(fontFamily = fontFamily), + headlineLarge = headlineLarge.copy(fontFamily = fontFamily), + headlineMedium = headlineMedium.copy(fontFamily = fontFamily), + headlineSmall = headlineSmall.copy(fontFamily = fontFamily), + titleLarge = titleLarge.copy(fontFamily = fontFamily), + titleMedium = titleMedium.copy(fontFamily = fontFamily), + titleSmall = titleSmall.copy(fontFamily = fontFamily), + bodyLarge = bodyLarge.copy(fontFamily = fontFamily), + bodyMedium = bodyMedium.copy(fontFamily = fontFamily), + bodySmall = bodySmall.copy(fontFamily = fontFamily), + labelLarge = labelLarge.copy(fontFamily = fontFamily), + labelMedium = labelMedium.copy(fontFamily = fontFamily), + labelSmall = labelSmall.copy(fontFamily = fontFamily) + ) +} + +@Composable +fun PasscodeKeyButtonStyle() = TextStyle( + fontFamily = LatoFonts(), + fontWeight = FontWeight.Bold, + fontSize = 24.sp +) + +@Composable +fun skipButtonStyle() = TextStyle( + color = blueTint, + fontSize = 20.sp, + fontFamily = LatoFonts() +) + +@Composable +fun forgotButtonStyle() = TextStyle( + color = blueTint, + fontSize = 14.sp, + fontFamily = LatoFonts() +) + +@Composable +fun useTouchIdButtonStyle() = TextStyle( + color = blueTint, + fontSize = 14.sp, + fontFamily = LatoFonts() +) \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/BioMetricUtil.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/BioMetricUtil.kt new file mode 100644 index 000000000..c1c4eb7bb --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/BioMetricUtil.kt @@ -0,0 +1,21 @@ +package com.mifos.passcode.utility + +interface BioMetricUtil { + + suspend fun setAndReturnPublicKey(): String? + suspend fun authenticate(): AuthenticationResult + fun canAuthenticate(): Boolean + fun generatePublicKey(): String? + fun signUserId(ucc: String): String + fun isBiometricSet(): Boolean + fun getPublicKey(): String? + fun isValidCrypto(): Boolean +} + +sealed class AuthenticationResult { + data object Success: AuthenticationResult() + data object Failed: AuthenticationResult() + data object AttemptExhausted: AuthenticationResult() + data object NegativeButtonClick: AuthenticationResult() + data class Error(val error: String): AuthenticationResult() +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/Constants.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/Constants.kt new file mode 100644 index 000000000..42932b28f --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/Constants.kt @@ -0,0 +1,7 @@ +package com.mifos.passcode.utility + +object Constants { + const val STEPS_COUNT = 2 + const val PASSCODE_LENGTH = 4 + const val VIBRATE_FEEDBACK_DURATION = 300L +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/PreferenceManager.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/PreferenceManager.kt new file mode 100644 index 000000000..a1a9ee4d4 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/PreferenceManager.kt @@ -0,0 +1,36 @@ +package com.mifos.passcode.utility + +import com.mifos.passcode.resources.Res +import com.mifos.passcode.resources.has_passcode +import com.mifos.passcode.resources.passcode +import com.russhwolf.settings.Settings + +/** + * @author pratyush + * @since 15/3/24 + */ + +class PreferenceManager() +{ + private val settings : Settings by lazy { + Settings() + } + + var hasPasscode: Boolean + get() = settings.getBoolean(Res.string.has_passcode.toString(), false) + set(value) = settings.putBoolean(Res.string.has_passcode.toString(), value) + + fun savePasscode(passcode: String) { + settings.putString(Res.string.passcode.toString(), passcode) + hasPasscode = true + } + + fun getSavedPasscode(): String { + return settings.getString(Res.string.passcode.toString(), "") + } + + fun clearPasscode() { + settings.clear() + hasPasscode = false + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/ShakeAnimation.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/ShakeAnimation.kt new file mode 100644 index 000000000..c9b483bed --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/ShakeAnimation.kt @@ -0,0 +1,28 @@ +package com.mifos.passcode.utility + +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.LinearOutSlowInEasing +import androidx.compose.animation.core.keyframes +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +object ShakeAnimation { + + fun CoroutineScope.performShakeAnimation(xShake: Animatable) { + launch { + xShake.animateTo( + targetValue = 0f, // This resets the position after the shake + animationSpec = keyframes { + durationMillis = 280 // Total animation duration + 0f at 0 with LinearOutSlowInEasing // Start position + 20f at 80 with LinearOutSlowInEasing // Move right + -20f at 120 with LinearOutSlowInEasing // Move left + 10f at 160 with LinearOutSlowInEasing // Move right + -10f at 200 with LinearOutSlowInEasing // Move left + 5f at 240 with LinearOutSlowInEasing // Move right + 0f at 280 // End at the original position + } + ) + } + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/Step.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/Step.kt new file mode 100644 index 000000000..a3de5330a --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/utility/Step.kt @@ -0,0 +1,6 @@ +package com.mifos.passcode.utility + +enum class Step(var index: Int) { + Create(0), + Confirm(1) +} diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/viewmodels/BiometricAuthorizationViewModel.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/viewmodels/BiometricAuthorizationViewModel.kt new file mode 100644 index 000000000..fae0e3db2 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/viewmodels/BiometricAuthorizationViewModel.kt @@ -0,0 +1,85 @@ +package com.mifos.passcode.viewmodels + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.mifos.passcode.data.SetBiometricPublicKeyRepository +import com.mifos.passcode.data.VerifyBiometric +import com.mifos.passcode.utility.AuthenticationResult +import com.mifos.passcode.utility.BioMetricUtil +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +class BiometricAuthorizationViewModel: ViewModel() { + private val setBiometricPublicKeyRepository = + SetBiometricPublicKeyRepository() + private val verifyBiometric = VerifyBiometric() + + private val _state: MutableStateFlow = + MutableStateFlow(BiometricState(false, null)) + private val _effect: MutableSharedFlow = MutableSharedFlow(replay = 0) + + val state: StateFlow + get() = _state + + val effect: SharedFlow + get() = _effect + + fun setBiometricAuthorization(bioMetricUtil: BioMetricUtil) { + viewModelScope.launch { + _state.value = BiometricState(isLoading = true, error = null) + if (!bioMetricUtil.canAuthenticate()) { + _state.value = BiometricState(isLoading = true, error = "Biometric not available") + return@launch + } + val publicKey = bioMetricUtil.setAndReturnPublicKey() ?: "" + setBiometricPublicKeyRepository.set(publicKey) + _state.value = BiometricState(isLoading = false, error = null) + _effect.emit(BiometricEffect.BiometricSetSuccess) + + } + } + + fun authorizeBiometric(bioMetricUtil: BioMetricUtil) { + viewModelScope.launch { + when(val biometricResult = bioMetricUtil.authenticate()) { + AuthenticationResult.AttemptExhausted -> { + _state.value = BiometricState(isLoading = false, error = "Attempt Exhausted") + } + is AuthenticationResult.Error -> { + _state.value = BiometricState(isLoading = false, error = biometricResult.error) + } + AuthenticationResult.Failed -> { + _state.value = BiometricState(isLoading = false, error = "Biometric Failed") + } + AuthenticationResult.NegativeButtonClick -> { + _state.value = BiometricState(isLoading = false, error = "Biometric Canceled") + } + AuthenticationResult.Success -> { + _state.value = BiometricState(isLoading = true, error = null) + val signedUserId = bioMetricUtil.signUserId("userId") + val result = verifyBiometric.verify(signedUserId) + if (result.isSuccess) { + _state.value = BiometricState(isLoading = false, error = null) + _effect.emit(BiometricEffect.BiometricAuthSuccess) + } else { + _state.value = BiometricState(isLoading = false, error = result.exceptionOrNull()!!.message) + } + } + } + + } + } +} + +data class BiometricState( + val isLoading: Boolean, + val error: String? +) + +sealed class BiometricEffect { + data object BiometricSetSuccess: BiometricEffect() + data object BiometricAuthSuccess: BiometricEffect() +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/viewmodels/PasscodeViewModel.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/viewmodels/PasscodeViewModel.kt new file mode 100644 index 000000000..961fbc33d --- /dev/null +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/viewmodels/PasscodeViewModel.kt @@ -0,0 +1,146 @@ +package com.mifos.passcode.viewmodels + +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.mifos.passcode.data.PasscodeRepositoryImpl +import com.mifos.passcode.utility.Constants.PASSCODE_LENGTH +import com.mifos.passcode.utility.PreferenceManager +import com.mifos.passcode.utility.Step +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +/** + * @author pratyush + * @since 15/3/24 + */ + +class PasscodeViewModel : + ViewModel() { + + private val + passcodeRepository = PasscodeRepositoryImpl(PreferenceManager()) + private val _onPasscodeConfirmed = MutableSharedFlow() + private val _onPasscodeRejected = MutableSharedFlow() + + private val _activeStep = MutableStateFlow(Step.Create) + private val _filledDots = MutableStateFlow(0) + + private var createPasscode: StringBuilder = StringBuilder() + private var confirmPasscode: StringBuilder = StringBuilder() + + val onPasscodeConfirmed = _onPasscodeConfirmed.asSharedFlow() + val onPasscodeRejected = _onPasscodeRejected.asSharedFlow() + + val activeStep = _activeStep.asStateFlow() + val filledDots = _filledDots.asStateFlow() + + private val _passcodeVisible = MutableStateFlow(false) + val passcodeVisible = _passcodeVisible.asStateFlow() + + private val _currentPasscodeInput = MutableStateFlow("") + val currentPasscodeInput = _currentPasscodeInput.asStateFlow() + + private var _isPasscodeAlreadySet = mutableStateOf(passcodeRepository.hasPasscode) + + init { + resetData() + } + + private fun emitActiveStep(activeStep: Step) = viewModelScope.launch { + _activeStep.emit(activeStep) + } + + private fun emitFilledDots(filledDots: Int) = viewModelScope.launch { + _filledDots.emit(filledDots) + } + + private fun emitOnPasscodeConfirmed(confirmPassword: String) = viewModelScope.launch { + _onPasscodeConfirmed.emit(confirmPassword) + } + + private fun emitOnPasscodeRejected() = viewModelScope.launch { + _onPasscodeRejected.emit(Unit) + } + + fun togglePasscodeVisibility() { + _passcodeVisible.value = !_passcodeVisible.value + } + + private fun resetData() { + emitActiveStep(Step.Create) + emitFilledDots(0) + + createPasscode.clear() + confirmPasscode.clear() + } + + fun enterKey(key: String) { + if (_filledDots.value >= PASSCODE_LENGTH) { + return + } + + val currentPasscode = + if (_activeStep.value == Step.Create) createPasscode else confirmPasscode + currentPasscode.append(key) + _currentPasscodeInput.value = currentPasscode.toString() + emitFilledDots(currentPasscode.length) + + if (_filledDots.value == PASSCODE_LENGTH) { + if (_isPasscodeAlreadySet.value) { + if (passcodeRepository.getSavedPasscode() == createPasscode.toString()) { + emitOnPasscodeConfirmed(createPasscode.toString()) + createPasscode.clear() + } else { + emitOnPasscodeRejected() + // logic for retires can be written here + } + _currentPasscodeInput.value = "" + } else if (_activeStep.value == Step.Create) { + emitActiveStep(Step.Confirm) + emitFilledDots(0) + _currentPasscodeInput.value = "" + } else { + if (createPasscode.toString() == confirmPasscode.toString()) { + emitOnPasscodeConfirmed(confirmPasscode.toString()) + passcodeRepository.savePasscode(confirmPasscode.toString()) + _isPasscodeAlreadySet.value = true + resetData() + } else { + emitOnPasscodeRejected() + resetData() + } + _currentPasscodeInput.value = "" + } + } + } + + fun deleteKey() { + val currentPasscode = + if (_activeStep.value == Step.Create) createPasscode else confirmPasscode + + if (currentPasscode.isNotEmpty()) { + currentPasscode.deleteAt(currentPasscode.length - 1) + _currentPasscodeInput.value = currentPasscode.toString() + emitFilledDots(currentPasscode.length) + } + } + + + fun deleteAllKeys() { + if (_activeStep.value == Step.Create) { + createPasscode.clear() + } else { + confirmPasscode.clear() + } + _currentPasscodeInput.value = "" + emitFilledDots(0) + } + + fun restart() { + resetData() + _passcodeVisible.value = false + } +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/CipherUtilImpl.desktop.kt b/libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/CipherUtilImpl.desktop.kt new file mode 100644 index 000000000..ad5e9747e --- /dev/null +++ b/libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/CipherUtilImpl.desktop.kt @@ -0,0 +1,5 @@ +package com.mifos.passcode + +actual class CommonKeyPair +actual interface CommonPublicKey +actual class Crypto \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/Platform.desktop.kt b/libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/Platform.desktop.kt new file mode 100644 index 000000000..d68c9277a --- /dev/null +++ b/libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/Platform.desktop.kt @@ -0,0 +1,4 @@ +package com.mifos.passcode + +actual fun getPlatform(): Platform { +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/App.ios.kt b/libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/App.ios.kt new file mode 100644 index 000000000..0c7470eb1 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/App.ios.kt @@ -0,0 +1,26 @@ +package com.mifos.passcode + +import androidx.compose.ui.window.ComposeUIViewController +import com.mifos.passcode.utility.BioMetricUtil +import com.mifos.passcode.component.PasscodeScreen +import com.mifos.passcode.viewmodels.BiometricAuthorizationViewModel +import platform.UIKit.UIViewController + +fun MainViewController( + bioMetricUtil: BioMetricUtil, + biometricViewModel: BiometricAuthorizationViewModel +): UIViewController = ComposeUIViewController { + PasscodeScreen( + onPasscodeConfirm = { + }, + onSkipButton = { + }, + onForgotButton = {}, + onPasscodeRejected = {}, + bioMetricUtil = bioMetricUtil, + biometricAuthorizationViewModel = biometricViewModel, + onBiometricAuthSuccess = { + }, + enableBiometric = true + ) +} \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/CipherUtilImpl.ios.kt b/libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/CipherUtilImpl.ios.kt new file mode 100644 index 000000000..3a8a65bee --- /dev/null +++ b/libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/CipherUtilImpl.ios.kt @@ -0,0 +1,12 @@ +package com.mifos.passcode + +import com.mifos.passcode.CommonPublicKey + +actual data class CommonKeyPair(val publicKey: String?, val privateKey: String?) +actual interface CommonPublicKey { + val encoded: String? +} +actual class Crypto + +data class CommonPublicKeyImpl(override val encoded: String): + com.mifos.passcode.CommonPublicKey \ No newline at end of file diff --git a/libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/Platform.ios.kt b/libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/Platform.ios.kt new file mode 100644 index 000000000..37447a6f7 --- /dev/null +++ b/libs/cmp-mifos-passcode/src/iosMain/kotlin/com/mifos/passcode/Platform.ios.kt @@ -0,0 +1,8 @@ +package com.mifos.passcode + +class IOSPlatform: com.mifos.passcode.Platform { +// override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion + override val name: String = "Ios" +} + +actual fun getPlatform(): com.mifos.passcode.Platform = IOSPlatform() \ No newline at end of file diff --git a/mifospay/build.gradle.kts b/mifospay/build.gradle.kts index 4460c2e83..04211119a 100644 --- a/mifospay/build.gradle.kts +++ b/mifospay/build.gradle.kts @@ -116,7 +116,7 @@ dependencies { implementation(projects.feature.standingInstruction) implementation(projects.feature.search) - implementation(projects.libs.mifosPasscode) + implementation(projects.libs.cmpMifosPasscode) implementation(projects.libs.material3Navigation) // Compose diff --git a/mifospay/src/main/java/org/mifospay/MainActivity.kt b/mifospay/src/main/java/org/mifospay/MainActivity.kt index 8416a5d33..b98508a74 100644 --- a/mifospay/src/main/java/org/mifospay/MainActivity.kt +++ b/mifospay/src/main/java/org/mifospay/MainActivity.kt @@ -10,7 +10,6 @@ package org.mifospay import android.os.Bundle -import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels @@ -21,12 +20,18 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.metrics.performance.JankStats import androidx.navigation.compose.rememberNavController +import com.mifos.passcode.BiometricUtilAndroidImpl +import com.mifos.passcode.CipherUtilAndroidImpl +import com.mifos.passcode.data.PasscodeRepository +import com.mifos.passcode.data.PasscodeRepositoryImpl +import com.mifos.passcode.utility.PreferenceManager import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.onEach @@ -47,7 +52,7 @@ import javax.inject.Inject @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) @AndroidEntryPoint -class MainActivity : ComponentActivity() { +class MainActivity : FragmentActivity() { /** * Lazily inject [JankStats], which is used to track jank throughout the app. @@ -66,6 +71,12 @@ class MainActivity : ComponentActivity() { private val viewModel: MainActivityViewModel by viewModels() + private val bioMetricUtil by lazy { + BiometricUtilAndroidImpl(this, CipherUtilAndroidImpl()) + } + + private lateinit var passcodeRepository: PasscodeRepository + override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() super.onCreate(savedInstanceState) @@ -88,6 +99,13 @@ class MainActivity : ComponentActivity() { } } + bioMetricUtil.preparePrompt( + title = getString(R.string.biometric_auth_title), + subtitle = "", + description = getString(R.string.biometric_auth_description), + ) + passcodeRepository = PasscodeRepositoryImpl(PreferenceManager()) + enableEdgeToEdge() setContent { @@ -128,6 +146,8 @@ class MainActivity : ComponentActivity() { } } }, + bioMetricUtil = bioMetricUtil, + enableBiometric = true, ) } } diff --git a/mifospay/src/main/java/org/mifospay/MainActivityViewModel.kt b/mifospay/src/main/java/org/mifospay/MainActivityViewModel.kt index 2a3677a51..5438e819a 100644 --- a/mifospay/src/main/java/org/mifospay/MainActivityViewModel.kt +++ b/mifospay/src/main/java/org/mifospay/MainActivityViewModel.kt @@ -18,14 +18,13 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import org.mifos.library.passcode.data.PasscodeManager import org.mifospay.core.data.repository.auth.UserDataRepository import javax.inject.Inject @HiltViewModel class MainActivityViewModel @Inject constructor( private val userDataRepository: UserDataRepository, - private val passcodeManager: PasscodeManager, +// private val passcodeManager: PasscodeManager, ) : ViewModel() { val uiState: StateFlow = userDataRepository.userData.map { @@ -39,7 +38,7 @@ class MainActivityViewModel @Inject constructor( fun logOut() { viewModelScope.launch { userDataRepository.logOut() - passcodeManager.clearPasscode() +// passcodeManager.clearPasscode() } } } diff --git a/mifospay/src/main/java/org/mifospay/navigation/LoginNavGraph.kt b/mifospay/src/main/java/org/mifospay/navigation/LoginNavGraph.kt index c80043848..4bb11f2b2 100644 --- a/mifospay/src/main/java/org/mifospay/navigation/LoginNavGraph.kt +++ b/mifospay/src/main/java/org/mifospay/navigation/LoginNavGraph.kt @@ -12,7 +12,7 @@ package org.mifospay.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.navigation -import org.mifos.library.passcode.navigateToPasscodeScreen +import com.mifos.passcode.navigateToPasscodeScreen import org.mifospay.common.Constants import org.mifospay.feature.auth.navigation.LOGIN_ROUTE import org.mifospay.feature.auth.navigation.loginScreen diff --git a/mifospay/src/main/java/org/mifospay/navigation/PasscodeNavGraph.kt b/mifospay/src/main/java/org/mifospay/navigation/PasscodeNavGraph.kt index a672817e2..14948cc07 100644 --- a/mifospay/src/main/java/org/mifospay/navigation/PasscodeNavGraph.kt +++ b/mifospay/src/main/java/org/mifospay/navigation/PasscodeNavGraph.kt @@ -12,10 +12,15 @@ package org.mifospay.navigation import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.navigation -import org.mifos.library.passcode.PASSCODE_SCREEN -import org.mifos.library.passcode.passcodeRoute +import com.mifos.passcode.PASSCODE_SCREEN +import com.mifos.passcode.passcodeRoute +import com.mifos.passcode.utility.BioMetricUtil -internal fun NavGraphBuilder.passcodeNavGraph(navController: NavController) { +internal fun NavGraphBuilder.passcodeNavGraph( + navController: NavController, + bioMetricUtil: BioMetricUtil, + enableBiometric: Boolean +) { navigation( route = MifosNavGraph.PASSCODE_GRAPH, startDestination = PASSCODE_SCREEN, @@ -37,6 +42,12 @@ internal fun NavGraphBuilder.passcodeNavGraph(navController: NavController) { navController.popBackStack() navController.navigate(MifosNavGraph.MAIN_GRAPH) }, + onBiometricAuthSucess = { + navController.popBackStack() + navController.navigate(MifosNavGraph.MAIN_GRAPH) + }, + bioMetricUtil = bioMetricUtil, + enableBiometric = enableBiometric, ) } } diff --git a/mifospay/src/main/java/org/mifospay/navigation/RootNavGraph.kt b/mifospay/src/main/java/org/mifospay/navigation/RootNavGraph.kt index 1a0735b28..38dc5db3a 100644 --- a/mifospay/src/main/java/org/mifospay/navigation/RootNavGraph.kt +++ b/mifospay/src/main/java/org/mifospay/navigation/RootNavGraph.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import com.mifos.passcode.utility.BioMetricUtil import org.mifospay.ui.MifosApp import org.mifospay.ui.MifosAppState @@ -22,8 +23,10 @@ internal fun RootNavGraph( appState: MifosAppState, navHostController: NavHostController, startDestination: String, + bioMetricUtil: BioMetricUtil, + enableBiometric: Boolean, onClickLogout: () -> Unit, - modifier: Modifier = Modifier, + modifier: Modifier = Modifier ) { NavHost( navController = navHostController, @@ -33,7 +36,11 @@ internal fun RootNavGraph( ) { loginNavGraph(navHostController) - passcodeNavGraph(navHostController) + passcodeNavGraph( + navHostController, + bioMetricUtil = bioMetricUtil, + enableBiometric = enableBiometric, + ) composable(MifosNavGraph.MAIN_GRAPH) { MifosApp( diff --git a/mifospay/src/main/res/values/strings.xml b/mifospay/src/main/res/values/strings.xml index 8334b94dc..052bfa7cf 100644 --- a/mifospay/src/main/res/values/strings.xml +++ b/mifospay/src/main/res/values/strings.xml @@ -16,5 +16,7 @@ Profile ⚠️ You aren’t connected to the internet FAQ - + Login + Unlock Mifos + Confirm your screen lock pattern, PIN, password, or fingerprint to unlock diff --git a/settings.gradle.kts b/settings.gradle.kts index f43a17c79..7c9e37527 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -78,6 +78,8 @@ include(":libs:country-code-picker") include(":libs:pullrefresh") include(":libs:material3-navigation") include(":libs:mifos-passcode") +include(":libs:cmp-mifos-passcode") include(":shared") include(":desktop") + diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 5c99a7fef..a08fcf95b 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -65,17 +65,19 @@ kotlin { implementation(compose.ui) implementation(compose.components.resources) implementation(compose.components.uiToolingPreview) - implementation(libs.kotlinx.datetime) implementation(libs.kotlinx.serialization.json) - api(libs.koin.core) implementation(libs.koin.compose) implementation(libs.koin.compose.viewmodel) - implementation(libs.datastore) + implementation(project(":libs:cmp-mifos-passcode")) + api(project(":libs:cmp-mifos-passcode")) } + iosMain.dependencies { + api(project(":libs:cmp-mifos-passcode")) + } val desktopMain by getting { dependencies { // Desktop specific dependencies @@ -117,4 +119,4 @@ android { dependencies { debugImplementation(compose.uiTooling) } -} +} \ No newline at end of file diff --git a/shared/src/iosMain/kotlin/org/mifospay/shared/preferences/DataStoreModule.ios.kt b/shared/src/iosMain/kotlin/org/mifospay/shared/preferences/DataStoreModule.ios.kt index 6e7e19d38..72f5d51fd 100644 --- a/shared/src/iosMain/kotlin/org/mifospay/shared/preferences/DataStoreModule.ios.kt +++ b/shared/src/iosMain/kotlin/org/mifospay/shared/preferences/DataStoreModule.ios.kt @@ -10,9 +10,13 @@ package org.mifospay.shared.preferences import androidx.datastore.core.DataStore +import kotlinx.cinterop.ExperimentalForeignApi import okio.FileSystem import okio.Path.Companion.toPath import org.mifospay.shared.commonMain.proto.UserPreferences +import platform.Foundation.NSDocumentDirectory +import platform.Foundation.NSFileManager +import platform.Foundation.NSUserDomainMask actual fun getDataStore(): DataStore { return createDataStore( @@ -21,6 +25,7 @@ actual fun getDataStore(): DataStore { ) } +@OptIn(ExperimentalForeignApi::class) private fun documentDirectory(): String { val documentDirectory = NSFileManager.defaultManager.URLForDirectory( directory = NSDocumentDirectory, From e40db8b0b70e9ead0ff5d5b3c188893ec6dbee9f Mon Sep 17 00:00:00 2001 From: akashmeruva9 Date: Thu, 12 Sep 2024 20:16:07 +0530 Subject: [PATCH 2/2] CMP Mifos Passcode Module Setup --- gradle/libs.versions.toml | 22 +- .../passcode/component/PasscodeToolbar.kt | 2 +- .../com/mifos/passcode/Platform.desktop.kt | 8 +- mifospay/build.gradle.kts | 1 + .../prodReleaseRuntimeClasspath.tree.txt | 366 +++++++++--------- .../prodReleaseRuntimeClasspath.txt | 9 +- .../mifospay/navigation/PasscodeNavGraph.kt | 2 +- .../org/mifospay/navigation/RootNavGraph.kt | 2 +- 8 files changed, 216 insertions(+), 196 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b1f32f51e..0705978e9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -agp = "8.3.0" accompanistPagerVersion = "0.34.0" activityVersion = "1.9.1" +agp = "8.3.0" androidDesugarJdkLibs = "2.0.4" androidGradlePlugin = "8.5.2" androidTools = "31.5.2" @@ -24,6 +24,7 @@ androidxNavigation = "2.8.0-rc01" androidxProfileinstaller = "1.3.1" androidxTracing = "1.3.0-alpha02" appcompatVersion = "1.7.0" +biometricKtx = "1.1.0" cameraLifecycleVersion = "1.3.4" cameraViewVersion = "1.3.4" coil = "2.6.0" @@ -59,9 +60,12 @@ ktorVersion = "2.3.4" libphonenumberAndroidVersion = "8.13.35" lifecycleExtensionsVersion = "2.2.0" lifecycleVersion = "2.8.4" +lifecycleViewmodelKtx = "2.8.1" logbackClassicVersion = "1.2.3" minSdk = "24" moduleGraph = "2.5.0" +multiplatformSettings = "1.0.0" +navigationComposeVersion = "2.7.0-alpha07" okHttp3Version = "4.12.0" playServicesAuthVersion = "21.2.0" playServicesCodeScanner = "16.1.0" @@ -82,10 +86,6 @@ twitter-detekt-compose = "0.0.26" versionCatalogLinterVersion = "1.0.3" wire = "5.0.0" zxingVersion = "3.5.3" -lifecycleViewmodelKtx = "2.8.1" -navigationComposeVersion = "2.7.0-alpha07" -multiplatformSettings = "1.0.0" -biometricKtx = "1.1.0" [libraries] accompanist-pager = { group = "com.google.accompanist", name = "accompanist-pager", version.ref = "accompanistPagerVersion" } @@ -95,6 +95,7 @@ android-tools-common = { group = "com.android.tools", name = "common", version.r androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityVersion" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompatVersion" } +androidx-biometric = { group = "androidx.biometric", name = "biometric", version.ref = "biometricKtx" } androidx-browser = { group = "androidx.browser", name = "browser", version.ref = "androidxBrowser" } androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "cameraLifecycleVersion" } androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraViewVersion" } @@ -128,6 +129,7 @@ androidx-lifecycle-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewm androidx-lifecycle-runtimeCompose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } androidx-lifecycle-runtimeTesting = { group = "androidx.lifecycle", name = "lifecycle-runtime-testing", version.ref = "androidxLifecycle" } androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "lifecycleViewmodelKtx" } androidx-metrics = { group = "androidx.metrics", name = "metrics-performance", version.ref = "androidxMetrics" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" } androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "androidxNavigation" } @@ -164,6 +166,7 @@ koin-compose-viewmodel = { group = "io.insert-koin", name = "koin-compose-viewmo koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" } kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" } +kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" } kotlinx-collections-immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinxImmutable" } kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } @@ -185,6 +188,8 @@ lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = lint-checks = { group = "com.android.tools.lint", name = "lint-checks", version.ref = "androidTools" } lint-tests = { group = "com.android.tools.lint", name = "lint-tests", version.ref = "androidTools" } logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logbackClassicVersion" } +multiplatform-settings-no-arg = { group = "com.russhwolf", name = "multiplatform-settings-no-arg", version.ref = "multiplatformSettings" } +navigation-compose = { group = "org.jetbrains.androidx.navigation", name = "navigation-compose", version.ref = "navigationComposeVersion" } play-services-auth = { group = "com.google.android.gms", name = "play-services-auth", version.ref = "playServicesAuthVersion" } protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" } protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" } @@ -203,11 +208,6 @@ squareup-retrofit2 = { group = "com.squareup.retrofit2", name = "retrofit", vers truth = { group = "com.google.truth", name = "truth", version.ref = "truth" } twitter-detekt-compose = { group = "com.twitter.compose.rules", name = "detekt", version.ref = "twitter-detekt-compose" } zxing = { group = "com.google.zxing", name = "core", version.ref = "zxingVersion" } -navigation-compose = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigationComposeVersion" } -multiplatform-settings-no-arg = { module = "com.russhwolf:multiplatform-settings-no-arg", version.ref = "multiplatformSettings" } -kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } -androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "biometricKtx" } -androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "lifecycleViewmodelKtx" } [bundles] androidx-compose-ui-test = [ @@ -257,4 +257,4 @@ room = { id = "androidx.room", version.ref = "room" } secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } spotless = { id = "com.diffplug.spotless", version.ref = "spotlessVersion" } version-catalog-linter = { id = "io.github.pemistahl.version-catalog-linter", version.ref = "versionCatalogLinterVersion" } -wire = { id = "com.squareup.wire", version.ref = "wire" } \ No newline at end of file +wire = { id = "com.squareup.wire", version.ref = "wire" } diff --git a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeToolbar.kt b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeToolbar.kt index 17d09357d..a5c83860f 100644 --- a/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeToolbar.kt +++ b/libs/cmp-mifos-passcode/src/commonMain/kotlin/com/mifos/passcode/component/PasscodeToolbar.kt @@ -37,7 +37,7 @@ fun PasscodeToolbar(activeStep: Step, hasPasscode: Boolean) { Row( modifier = Modifier .fillMaxWidth() - .padding(top = 8.dp), + .padding(top = 40.dp), horizontalArrangement = Arrangement.Center ) { if (!hasPasscode) { diff --git a/libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/Platform.desktop.kt b/libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/Platform.desktop.kt index d68c9277a..aa93c4683 100644 --- a/libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/Platform.desktop.kt +++ b/libs/cmp-mifos-passcode/src/desktopMain/kotlin/com/mifos/passcode/Platform.desktop.kt @@ -1,4 +1,8 @@ package com.mifos.passcode -actual fun getPlatform(): Platform { -} \ No newline at end of file +class DesktopPlatform: com.mifos.passcode.Platform { + // override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion + override val name: String = "Desktop" +} + +actual fun getPlatform(): Platform = DesktopPlatform() \ No newline at end of file diff --git a/mifospay/build.gradle.kts b/mifospay/build.gradle.kts index 04211119a..e4272d58b 100644 --- a/mifospay/build.gradle.kts +++ b/mifospay/build.gradle.kts @@ -7,6 +7,7 @@ * * See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md */ + import org.mifospay.MifosBuildType /* diff --git a/mifospay/dependencies/prodReleaseRuntimeClasspath.tree.txt b/mifospay/dependencies/prodReleaseRuntimeClasspath.tree.txt index b96d6758e..4414f0cc8 100644 --- a/mifospay/dependencies/prodReleaseRuntimeClasspath.tree.txt +++ b/mifospay/dependencies/prodReleaseRuntimeClasspath.tree.txt @@ -230,9 +230,9 @@ | +--- androidx.compose.animation:animation-graphics:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.ui:ui-geometry:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.animation:animation-core:1.6.8 -> 1.7.0-rc01 (c) -| +--- androidx.compose.foundation:foundation-layout-android:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.runtime:runtime-android:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.ui:ui-android:1.6.8 -> 1.7.0-rc01 (c) +| +--- androidx.compose.foundation:foundation-layout-android:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.runtime:runtime-saveable-android:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.material3:material3-android:1.2.1 (c) | +--- androidx.compose.material:material-icons-extended-android:1.6.8 (c) @@ -243,11 +243,11 @@ | +--- androidx.compose.ui:ui-text:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.material:material-icons-core:1.6.8 (c) | +--- androidx.compose.material:material-ripple:1.6.8 (c) +| +--- androidx.compose.ui:ui-geometry-android:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.ui:ui-unit-android:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.ui:ui-util-android:1.6.8 -> 1.7.0-rc01 (c) -| +--- androidx.compose.foundation:foundation-android:1.6.8 -> 1.7.0-rc01 (c) -| +--- androidx.compose.ui:ui-geometry-android:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.ui:ui-tooling-preview-android:1.6.8 -> 1.7.0-rc01 (c) +| +--- androidx.compose.foundation:foundation-android:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.ui:ui-graphics-android:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.ui:ui-text-android:1.6.8 -> 1.7.0-rc01 (c) | +--- androidx.compose.material:material-icons-core-android:1.6.8 (c) @@ -873,6 +873,7 @@ | | | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c) | | | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c) | | | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c) +| | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c) | | | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c) | | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c) | | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.8.4 (c) @@ -881,7 +882,6 @@ | | | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (c) | | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c) | | | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c) -| | | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c) | | | | \--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c) | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.6.1 -> 2.8.4 (*) | | | +--- androidx.profileinstaller:profileinstaller:1.3.1 (*) @@ -965,6 +965,7 @@ | | | +--- androidx.lifecycle:lifecycle-common:2.8.4 (c) | | | +--- androidx.lifecycle:lifecycle-common-java8:2.8.4 (c) | | | +--- androidx.lifecycle:lifecycle-livedata:2.8.4 (c) +| | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c) | | | +--- androidx.lifecycle:lifecycle-process:2.8.4 (c) | | | +--- androidx.lifecycle:lifecycle-runtime:2.8.4 (c) | | | +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (c) @@ -973,107 +974,194 @@ | | | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.4 (c) | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4 (c) | | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4 (c) -| | | +--- androidx.lifecycle:lifecycle-livedata-core:2.8.4 (c) | | | \--- androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4 (c) | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.24 -> 2.0.20 (*) | +--- io.insert-koin:koin-core:3.6.0-Beta4 (*) +| +--- project :libs:cmp-mifos-passcode +| | +--- androidx.biometric:biometric:1.1.0 +| | | +--- androidx.activity:activity:1.1.0 -> 1.9.1 (*) +| | | +--- androidx.appcompat:appcompat:1.2.0 -> 1.7.0 (*) +| | | +--- androidx.lifecycle:lifecycle-livedata-core:2.2.0 -> 2.8.4 (*) +| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.2.0 -> 2.8.4 (*) +| | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) +| | | +--- androidx.core:core:1.3.2 -> 1.13.1 (*) +| | | \--- androidx.fragment:fragment:1.2.5 -> 1.7.1 (*) +| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*) +| | +--- androidx.lifecycle:lifecycle-viewmodel:2.8.1 -> 2.8.4 (*) +| | +--- org.jetbrains.compose.ui:ui:1.6.11 +| | | \--- androidx.compose.ui:ui:1.6.7 -> 1.7.0-rc01 (*) +| | +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*) +| | +--- org.jetbrains.compose.foundation:foundation:1.6.11 +| | | \--- androidx.compose.foundation:foundation:1.6.7 -> 1.7.0-rc01 +| | | \--- androidx.compose.foundation:foundation-android:1.7.0-rc01 +| | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) +| | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*) +| | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*) +| | | +--- androidx.compose.animation:animation:1.7.0-rc01 +| | | | \--- androidx.compose.animation:animation-android:1.7.0-rc01 +| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) +| | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*) +| | | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*) +| | | | +--- androidx.compose.animation:animation-core:1.7.0-rc01 +| | | | | \--- androidx.compose.animation:animation-core-android:1.7.0-rc01 +| | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) +| | | | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*) +| | | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*) +| | | | | +--- androidx.compose.ui:ui:1.6.0 -> 1.7.0-rc01 (*) +| | | | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (*) +| | | | | +--- androidx.compose.ui:ui-unit:1.6.0 -> 1.7.0-rc01 (*) +| | | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*) +| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*) +| | | | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.1 (*) +| | | | | +--- androidx.compose.animation:animation:1.7.0-rc01 (c) +| | | | | \--- androidx.compose.animation:animation-graphics:1.7.0-rc01 (c) +| | | | +--- androidx.compose.foundation:foundation-layout:1.6.0 -> 1.7.0-rc01 +| | | | | \--- androidx.compose.foundation:foundation-layout-android:1.7.0-rc01 +| | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) +| | | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*) +| | | | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*) +| | | | | +--- androidx.compose.animation:animation-core:1.2.1 -> 1.7.0-rc01 (*) +| | | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*) +| | | | | +--- androidx.compose.ui:ui:1.6.0 -> 1.7.0-rc01 (*) +| | | | | +--- androidx.compose.ui:ui-unit:1.7.0-rc01 (*) +| | | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*) +| | | | | +--- androidx.core:core:1.7.0 -> 1.13.1 (*) +| | | | | \--- androidx.compose.foundation:foundation:1.7.0-rc01 (c) +| | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*) +| | | | +--- androidx.compose.ui:ui:1.7.0-rc01 (*) +| | | | +--- androidx.compose.ui:ui-geometry:1.6.0 -> 1.7.0-rc01 (*) +| | | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (*) +| | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*) +| | | | +--- androidx.compose.animation:animation-core:1.7.0-rc01 (c) +| | | | \--- androidx.compose.animation:animation-graphics:1.7.0-rc01 (c) +| | | +--- androidx.compose.foundation:foundation-layout:1.7.0-rc01 (*) +| | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*) +| | | +--- androidx.compose.ui:ui:1.7.0-rc01 (*) +| | | +--- androidx.compose.ui:ui-text:1.6.0 -> 1.7.0-rc01 (*) +| | | +--- androidx.compose.ui:ui-util:1.6.0 -> 1.7.0-rc01 (*) +| | | +--- androidx.core:core:1.13.1 (*) +| | | +--- androidx.emoji2:emoji2:1.3.0 (*) +| | | \--- androidx.compose.foundation:foundation-layout:1.7.0-rc01 (c) +| | +--- org.jetbrains.compose.material3:material3:1.6.11 +| | | \--- androidx.compose.material3:material3:1.2.1 +| | | \--- androidx.compose.material3:material3-android:1.2.1 +| | | +--- androidx.activity:activity-compose:1.5.0 -> 1.9.1 (*) +| | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) +| | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*) +| | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*) +| | | +--- androidx.compose.animation:animation-core:1.6.0 -> 1.7.0-rc01 (*) +| | | +--- androidx.compose.foundation:foundation:1.6.0 -> 1.7.0-rc01 (*) +| | | +--- androidx.compose.foundation:foundation-layout:1.6.0 -> 1.7.0-rc01 (*) +| | | +--- androidx.compose.material:material-icons-core:1.6.0 -> 1.6.8 +| | | | \--- androidx.compose.material:material-icons-core-android:1.6.8 +| | | | +--- androidx.compose.ui:ui:1.6.8 -> 1.7.0-rc01 (*) +| | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*) +| | | | +--- androidx.compose.material:material-icons-extended:1.6.8 (c) +| | | | \--- androidx.compose.material:material-ripple:1.6.8 (c) +| | | +--- androidx.compose.material:material-ripple:1.6.0 -> 1.6.8 +| | | | \--- androidx.compose.material:material-ripple-android:1.6.8 +| | | | +--- androidx.compose.animation:animation:1.6.8 -> 1.7.0-rc01 (*) +| | | | +--- androidx.compose.foundation:foundation:1.6.8 -> 1.7.0-rc01 (*) +| | | | +--- androidx.compose.runtime:runtime:1.6.8 -> 1.7.0-rc01 (*) +| | | | +--- androidx.compose.ui:ui-util:1.6.8 -> 1.7.0-rc01 (*) +| | | | +--- androidx.compose.material:material-icons-core:1.6.8 (c) +| | | | \--- androidx.compose.material:material-icons-extended:1.6.8 (c) +| | | +--- androidx.compose.runtime:runtime:1.6.0 -> 1.7.0-rc01 (*) +| | | +--- androidx.compose.ui:ui-graphics:1.6.0 -> 1.7.0-rc01 (*) +| | | +--- androidx.compose.ui:ui-text:1.6.0 -> 1.7.0-rc01 (*) +| | | +--- androidx.compose.ui:ui-util:1.6.0 -> 1.7.0-rc01 (*) +| | | +--- androidx.lifecycle:lifecycle-common-java8:2.6.1 -> 2.8.4 (*) +| | | +--- androidx.lifecycle:lifecycle-runtime:2.6.1 -> 2.8.4 (*) +| | | +--- androidx.lifecycle:lifecycle-viewmodel:2.6.1 -> 2.8.4 (*) +| | | +--- androidx.savedstate:savedstate-ktx:1.2.1 (*) +| | | \--- androidx.compose.material3:material3-window-size-class:1.2.1 (c) +| | +--- org.jetbrains.compose.components:components-resources:1.6.11 +| | | \--- org.jetbrains.compose.components:components-resources-android:1.6.11 +| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.9.23 -> 2.0.20 (*) +| | | +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*) +| | | +--- org.jetbrains.compose.foundation:foundation:1.6.11 (*) +| | | \--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0 -> 1.8.1 (*) +| | +--- org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha07 +| | | \--- androidx.navigation:navigation-compose:2.7.7 -> 2.8.0-rc01 +| | | +--- androidx.activity:activity-compose:1.8.0 -> 1.9.1 (*) +| | | +--- androidx.compose.animation:animation:1.7.0-rc01 (*) +| | | +--- androidx.compose.foundation:foundation-layout:1.7.0-rc01 (*) +| | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*) +| | | +--- androidx.compose.runtime:runtime-saveable:1.7.0-rc01 (*) +| | | +--- androidx.compose.ui:ui:1.7.0-rc01 (*) +| | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2 -> 2.8.4 (*) +| | | +--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 +| | | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 +| | | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 +| | | | | | +--- androidx.annotation:annotation:1.8.1 (*) +| | | | | | +--- androidx.collection:collection-ktx:1.4.2 (*) +| | | | | | +--- androidx.core:core-ktx:1.1.0 -> 1.13.1 (*) +| | | | | | +--- androidx.lifecycle:lifecycle-common:2.6.2 -> 2.8.4 (*) +| | | | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -> 2.8.4 (*) +| | | | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2 -> 2.8.4 (*) +| | | | | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2 -> 2.8.4 (*) +| | | | | | +--- androidx.profileinstaller:profileinstaller:1.3.1 (*) +| | | | | | +--- androidx.savedstate:savedstate-ktx:1.2.1 (*) +| | | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*) +| | | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 -> 1.7.1 +| | | | | | | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.1 +| | | | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -> 2.0.20 (*) +| | | | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.1 +| | | | | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.1 (c) +| | | | | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.1 (c) +| | | | | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1 (c) +| | | | | | | | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.1 (c) +| | | | | | | \--- org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0 -> 2.0.20 +| | | | | | | \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*) +| | | | | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c) +| | | | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c) +| | | | | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c) +| | | | | | \--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c) +| | | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 (c) +| | | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c) +| | | | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c) +| | | | | \--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c) +| | | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 +| | | | | +--- androidx.activity:activity-ktx:1.7.1 -> 1.9.1 (*) +| | | | | +--- androidx.annotation:annotation-experimental:1.4.1 (*) +| | | | | +--- androidx.collection:collection:1.4.2 (*) +| | | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -> 2.8.4 (*) +| | | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2 -> 2.8.4 (*) +| | | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 (*) +| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*) +| | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 -> 1.7.1 (*) +| | | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 (c) +| | | | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c) +| | | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c) +| | | | | \--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c) +| | | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c) +| | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c) +| | | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c) +| | | | \--- androidx.navigation:navigation-common:2.8.0-rc01 (c) +| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*) +| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 -> 1.7.1 (*) +| | | +--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c) +| | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c) +| | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c) +| | | \--- androidx.navigation:navigation-common:2.8.0-rc01 (c) +| | +--- com.russhwolf:multiplatform-settings-no-arg:1.0.0 +| | | \--- com.russhwolf:multiplatform-settings-no-arg-android:1.0.0 +| | | +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0 -> 1.9.20 (*) +| | | +--- androidx.startup:startup-runtime:1.1.1 (*) +| | | \--- com.russhwolf:multiplatform-settings:1.0.0 +| | | \--- com.russhwolf:multiplatform-settings-android:1.0.0 +| | | \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0 -> 1.9.20 (*) +| | +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*) +| | \--- androidx.compose.ui:ui:1.6.8 -> 1.7.0-rc01 (*) | +--- com.squareup.wire:wire-runtime:5.0.0 | | \--- com.squareup.wire:wire-runtime-jvm:5.0.0 | | +--- com.squareup.okio:okio:3.9.0 (*) | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.24 -> 2.0.20 (*) | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*) | +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*) -| +--- org.jetbrains.compose.material3:material3:1.6.11 -| | \--- androidx.compose.material3:material3:1.2.1 -| | \--- androidx.compose.material3:material3-android:1.2.1 -| | +--- androidx.activity:activity-compose:1.5.0 -> 1.9.1 (*) -| | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) -| | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*) -| | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*) -| | +--- androidx.compose.animation:animation-core:1.6.0 -> 1.7.0-rc01 -| | | \--- androidx.compose.animation:animation-core-android:1.7.0-rc01 -| | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) -| | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*) -| | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*) -| | | +--- androidx.compose.ui:ui:1.6.0 -> 1.7.0-rc01 (*) -| | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (*) -| | | +--- androidx.compose.ui:ui-unit:1.6.0 -> 1.7.0-rc01 (*) -| | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*) -| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*) -| | | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3 -> 1.8.1 (*) -| | | +--- androidx.compose.animation:animation:1.7.0-rc01 (c) -| | | \--- androidx.compose.animation:animation-graphics:1.7.0-rc01 (c) -| | +--- androidx.compose.foundation:foundation:1.6.0 -> 1.7.0-rc01 -| | | \--- androidx.compose.foundation:foundation-android:1.7.0-rc01 -| | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) -| | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*) -| | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*) -| | | +--- androidx.compose.animation:animation:1.7.0-rc01 -| | | | \--- androidx.compose.animation:animation-android:1.7.0-rc01 -| | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) -| | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*) -| | | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*) -| | | | +--- androidx.compose.animation:animation-core:1.7.0-rc01 (*) -| | | | +--- androidx.compose.foundation:foundation-layout:1.6.0 -> 1.7.0-rc01 -| | | | | \--- androidx.compose.foundation:foundation-layout-android:1.7.0-rc01 -| | | | | +--- androidx.annotation:annotation:1.1.0 -> 1.8.1 (*) -| | | | | +--- androidx.annotation:annotation-experimental:1.4.0 -> 1.4.1 (*) -| | | | | +--- androidx.collection:collection:1.4.0 -> 1.4.2 (*) -| | | | | +--- androidx.compose.animation:animation-core:1.2.1 -> 1.7.0-rc01 (*) -| | | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*) -| | | | | +--- androidx.compose.ui:ui:1.6.0 -> 1.7.0-rc01 (*) -| | | | | +--- androidx.compose.ui:ui-unit:1.7.0-rc01 (*) -| | | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*) -| | | | | +--- androidx.core:core:1.7.0 -> 1.13.1 (*) -| | | | | \--- androidx.compose.foundation:foundation:1.7.0-rc01 (c) -| | | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*) -| | | | +--- androidx.compose.ui:ui:1.7.0-rc01 (*) -| | | | +--- androidx.compose.ui:ui-geometry:1.6.0 -> 1.7.0-rc01 (*) -| | | | +--- androidx.compose.ui:ui-graphics:1.7.0-rc01 (*) -| | | | +--- androidx.compose.ui:ui-util:1.7.0-rc01 (*) -| | | | +--- androidx.compose.animation:animation-core:1.7.0-rc01 (c) -| | | | \--- androidx.compose.animation:animation-graphics:1.7.0-rc01 (c) -| | | +--- androidx.compose.foundation:foundation-layout:1.7.0-rc01 (*) -| | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*) -| | | +--- androidx.compose.ui:ui:1.7.0-rc01 (*) -| | | +--- androidx.compose.ui:ui-text:1.6.0 -> 1.7.0-rc01 (*) -| | | +--- androidx.compose.ui:ui-util:1.6.0 -> 1.7.0-rc01 (*) -| | | +--- androidx.core:core:1.13.1 (*) -| | | +--- androidx.emoji2:emoji2:1.3.0 (*) -| | | \--- androidx.compose.foundation:foundation-layout:1.7.0-rc01 (c) -| | +--- androidx.compose.foundation:foundation-layout:1.6.0 -> 1.7.0-rc01 (*) -| | +--- androidx.compose.material:material-icons-core:1.6.0 -> 1.6.8 -| | | \--- androidx.compose.material:material-icons-core-android:1.6.8 -| | | +--- androidx.compose.ui:ui:1.6.8 -> 1.7.0-rc01 (*) -| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*) -| | | +--- androidx.compose.material:material-icons-extended:1.6.8 (c) -| | | \--- androidx.compose.material:material-ripple:1.6.8 (c) -| | +--- androidx.compose.material:material-ripple:1.6.0 -> 1.6.8 -| | | \--- androidx.compose.material:material-ripple-android:1.6.8 -| | | +--- androidx.compose.animation:animation:1.6.8 -> 1.7.0-rc01 (*) -| | | +--- androidx.compose.foundation:foundation:1.6.8 -> 1.7.0-rc01 (*) -| | | +--- androidx.compose.runtime:runtime:1.6.8 -> 1.7.0-rc01 (*) -| | | +--- androidx.compose.ui:ui-util:1.6.8 -> 1.7.0-rc01 (*) -| | | +--- androidx.compose.material:material-icons-core:1.6.8 (c) -| | | \--- androidx.compose.material:material-icons-extended:1.6.8 (c) -| | +--- androidx.compose.runtime:runtime:1.6.0 -> 1.7.0-rc01 (*) -| | +--- androidx.compose.ui:ui-graphics:1.6.0 -> 1.7.0-rc01 (*) -| | +--- androidx.compose.ui:ui-text:1.6.0 -> 1.7.0-rc01 (*) -| | +--- androidx.compose.ui:ui-util:1.6.0 -> 1.7.0-rc01 (*) -| | +--- androidx.lifecycle:lifecycle-common-java8:2.6.1 -> 2.8.4 (*) -| | +--- androidx.lifecycle:lifecycle-runtime:2.6.1 -> 2.8.4 (*) -| | +--- androidx.lifecycle:lifecycle-viewmodel:2.6.1 -> 2.8.4 (*) -| | +--- androidx.savedstate:savedstate-ktx:1.2.1 (*) -| | \--- androidx.compose.material3:material3-window-size-class:1.2.1 (c) -| +--- org.jetbrains.compose.ui:ui:1.6.11 -| | \--- androidx.compose.ui:ui:1.6.7 -> 1.7.0-rc01 (*) -| +--- org.jetbrains.compose.components:components-resources:1.6.11 -| | \--- org.jetbrains.compose.components:components-resources-android:1.6.11 -| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.9.23 -> 2.0.20 (*) -| | +--- org.jetbrains.compose.runtime:runtime:1.6.11 (*) -| | +--- org.jetbrains.compose.foundation:foundation:1.6.11 -| | | \--- androidx.compose.foundation:foundation:1.6.7 -> 1.7.0-rc01 (*) -| | \--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0 -> 1.8.1 (*) +| +--- org.jetbrains.compose.material3:material3:1.6.11 (*) +| +--- org.jetbrains.compose.ui:ui:1.6.11 (*) +| +--- org.jetbrains.compose.components:components-resources:1.6.11 (*) | +--- org.jetbrains.compose.components:components-ui-tooling-preview:1.6.11 | | \--- org.jetbrains.compose.components:components-ui-tooling-preview-android:1.6.11 | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.23 -> 2.0.20 (*) @@ -1083,77 +1171,15 @@ | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1 | | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.1 | | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -> 2.0.20 (*) -| | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.1 -| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.1 (c) -| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.1 (c) -| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1 (c) -| | | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.1 (c) -| | \--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.1 -| | \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.1 -| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 -> 2.0.20 (*) -| | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.1 (*) -| | \--- org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0 -> 2.0.20 -| | \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*) +| | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.1 (*) +| | \--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.1 (*) | +--- io.insert-koin:koin-compose:1.2.0-Beta4 (*) | +--- io.insert-koin:koin-compose-viewmodel:1.2.0-Beta4 | | \--- io.insert-koin:koin-compose-viewmodel-jvm:1.2.0-Beta4 | | +--- io.insert-koin:koin-compose:1.2.0-Beta4 (*) | | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0-rc03 | | | \--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0 -> 2.8.4 (*) -| | +--- org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha06 -| | | \--- androidx.navigation:navigation-compose:2.7.7 -> 2.8.0-rc01 -| | | +--- androidx.activity:activity-compose:1.8.0 -> 1.9.1 (*) -| | | +--- androidx.compose.animation:animation:1.7.0-rc01 (*) -| | | +--- androidx.compose.foundation:foundation-layout:1.7.0-rc01 (*) -| | | +--- androidx.compose.runtime:runtime:1.7.0-rc01 (*) -| | | +--- androidx.compose.runtime:runtime-saveable:1.7.0-rc01 (*) -| | | +--- androidx.compose.ui:ui:1.7.0-rc01 (*) -| | | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2 -> 2.8.4 (*) -| | | +--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 -| | | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 -| | | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 -| | | | | | +--- androidx.annotation:annotation:1.8.1 (*) -| | | | | | +--- androidx.collection:collection-ktx:1.4.2 (*) -| | | | | | +--- androidx.core:core-ktx:1.1.0 -> 1.13.1 (*) -| | | | | | +--- androidx.lifecycle:lifecycle-common:2.6.2 -> 2.8.4 (*) -| | | | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -> 2.8.4 (*) -| | | | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2 -> 2.8.4 (*) -| | | | | | +--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2 -> 2.8.4 (*) -| | | | | | +--- androidx.profileinstaller:profileinstaller:1.3.1 (*) -| | | | | | +--- androidx.savedstate:savedstate-ktx:1.2.1 (*) -| | | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*) -| | | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 -> 1.7.1 (*) -| | | | | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c) -| | | | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c) -| | | | | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c) -| | | | | | \--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c) -| | | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 (c) -| | | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c) -| | | | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c) -| | | | | \--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c) -| | | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 -| | | | | +--- androidx.activity:activity-ktx:1.7.1 -> 1.9.1 (*) -| | | | | +--- androidx.annotation:annotation-experimental:1.4.1 (*) -| | | | | +--- androidx.collection:collection:1.4.2 (*) -| | | | | +--- androidx.lifecycle:lifecycle-runtime-ktx:2.6.2 -> 2.8.4 (*) -| | | | | +--- androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2 -> 2.8.4 (*) -| | | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 (*) -| | | | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*) -| | | | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 -> 1.7.1 (*) -| | | | | +--- androidx.navigation:navigation-common:2.8.0-rc01 (c) -| | | | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c) -| | | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c) -| | | | | \--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c) -| | | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c) -| | | | +--- androidx.navigation:navigation-compose:2.8.0-rc01 (c) -| | | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c) -| | | | \--- androidx.navigation:navigation-common:2.8.0-rc01 (c) -| | | +--- org.jetbrains.kotlin:kotlin-stdlib:1.8.22 -> 2.0.20 (*) -| | | +--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.3 -> 1.7.1 (*) -| | | +--- androidx.navigation:navigation-runtime-ktx:2.8.0-rc01 (c) -| | | +--- androidx.navigation:navigation-runtime:2.8.0-rc01 (c) -| | | +--- androidx.navigation:navigation-common-ktx:2.8.0-rc01 (c) -| | | \--- androidx.navigation:navigation-common:2.8.0-rc01 (c) +| | +--- org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha06 -> 2.7.0-alpha07 (*) | | \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.24 -> 2.0.20 (*) | +--- androidx.datastore:datastore-core-okio:1.1.1 (*) | \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.20 (*) @@ -1998,23 +2024,7 @@ | +--- androidx.compose:compose-bom:2024.08.00 (*) | +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*) | \--- project :core:data (*) -+--- project :libs:mifos-passcode -| +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 (*) -| +--- androidx.tracing:tracing-ktx:1.3.0-alpha02 (*) -| +--- androidx.compose:compose-bom:2024.08.00 (*) -| +--- androidx.compose.ui:ui-tooling-preview -> 1.7.0-rc01 (*) -| +--- com.google.dagger:hilt-android:2.52 (*) -| +--- androidx.core:core-ktx:1.13.1 (*) -| +--- androidx.compose.foundation:foundation -> 1.7.0-rc01 (*) -| +--- androidx.compose.foundation:foundation-layout -> 1.7.0-rc01 (*) -| +--- androidx.compose.material:material-icons-extended -> 1.6.8 (*) -| +--- androidx.compose.material3:material3 -> 1.2.1 (*) -| +--- androidx.compose.runtime:runtime:1.6.8 -> 1.7.0-rc01 (*) -| +--- androidx.compose.ui:ui-util:1.6.8 -> 1.7.0-rc01 (*) -| +--- androidx.lifecycle:lifecycle-runtime-compose:2.8.4 (*) -| +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4 (*) -| +--- androidx.navigation:navigation-compose:2.8.0-rc01 (*) -| \--- androidx.hilt:hilt-navigation-compose:1.2.0 (*) ++--- project :libs:cmp-mifos-passcode (*) +--- project :libs:material3-navigation (*) +--- androidx.core:core-ktx:1.13.1 (*) +--- androidx.appcompat:appcompat:1.7.0 (*) diff --git a/mifospay/dependencies/prodReleaseRuntimeClasspath.txt b/mifospay/dependencies/prodReleaseRuntimeClasspath.txt index 6dc1ab157..3455cac25 100644 --- a/mifospay/dependencies/prodReleaseRuntimeClasspath.txt +++ b/mifospay/dependencies/prodReleaseRuntimeClasspath.txt @@ -30,9 +30,9 @@ :feature:settings :feature:standing-instruction :feature:upi-setup +:libs:cmp-mifos-passcode :libs:country-code-picker :libs:material3-navigation -:libs:mifos-passcode :libs:pullrefresh :shared androidx.activity:activity-compose:1.9.1 @@ -46,6 +46,7 @@ androidx.appcompat:appcompat:1.7.0 androidx.arch.core:core-common:2.2.0 androidx.arch.core:core-runtime:2.2.0 androidx.autofill:autofill:1.0.0 +androidx.biometric:biometric:1.1.0 androidx.browser:browser:1.8.0 androidx.camera:camera-core:1.3.4 androidx.camera:camera-lifecycle:1.3.4 @@ -263,6 +264,10 @@ com.google.zxing:core:3.5.3 com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0 com.maxkeppeler.sheets-compose-dialogs:calendar:1.3.0 com.maxkeppeler.sheets-compose-dialogs:core:1.3.0 +com.russhwolf:multiplatform-settings-android:1.0.0 +com.russhwolf:multiplatform-settings-no-arg-android:1.0.0 +com.russhwolf:multiplatform-settings-no-arg:1.0.0 +com.russhwolf:multiplatform-settings:1.0.0 com.squareup.okhttp3:logging-interceptor:4.12.0 com.squareup.okhttp3:okhttp:4.12.0 com.squareup.okio:okio-jvm:3.9.0 @@ -324,7 +329,7 @@ jakarta.inject:jakarta.inject-api:2.0.1 javax.inject:javax.inject:1 org.checkerframework:checker-qual:3.12.0 org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0-rc03 -org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha06 +org.jetbrains.androidx.navigation:navigation-compose:2.7.0-alpha07 org.jetbrains.compose.components:components-resources-android:1.6.11 org.jetbrains.compose.components:components-resources:1.6.11 org.jetbrains.compose.components:components-ui-tooling-preview-android:1.6.11 diff --git a/mifospay/src/main/java/org/mifospay/navigation/PasscodeNavGraph.kt b/mifospay/src/main/java/org/mifospay/navigation/PasscodeNavGraph.kt index 14948cc07..eaaa8bcdf 100644 --- a/mifospay/src/main/java/org/mifospay/navigation/PasscodeNavGraph.kt +++ b/mifospay/src/main/java/org/mifospay/navigation/PasscodeNavGraph.kt @@ -19,7 +19,7 @@ import com.mifos.passcode.utility.BioMetricUtil internal fun NavGraphBuilder.passcodeNavGraph( navController: NavController, bioMetricUtil: BioMetricUtil, - enableBiometric: Boolean + enableBiometric: Boolean, ) { navigation( route = MifosNavGraph.PASSCODE_GRAPH, diff --git a/mifospay/src/main/java/org/mifospay/navigation/RootNavGraph.kt b/mifospay/src/main/java/org/mifospay/navigation/RootNavGraph.kt index 38dc5db3a..a15b95036 100644 --- a/mifospay/src/main/java/org/mifospay/navigation/RootNavGraph.kt +++ b/mifospay/src/main/java/org/mifospay/navigation/RootNavGraph.kt @@ -26,7 +26,7 @@ internal fun RootNavGraph( bioMetricUtil: BioMetricUtil, enableBiometric: Boolean, onClickLogout: () -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { NavHost( navController = navHostController,