From 467c363f706ac62edec92339490bbd25430d4411 Mon Sep 17 00:00:00 2001 From: akashmeruva9 Date: Mon, 8 Jul 2024 23:10:36 +0530 Subject: [PATCH] # This is a combination of 2 commits. # This is the 1st commit message: refactor: User Profile Module # This is the commit message #2: refactor: savings module (#2635) refactor: qr module (#2638) refactor: transfer process module (#2644) Refactor login module (#2639) * refactor: Login module * refactor: Login module refactor: Account Module (#2640) refactor: Client Charge Module (#2641) refactor: Recent Transaction Module (#2642) refactor: third party transfer module (#2643) refactor: help module (#2645) refactor: notification module (#2646) refactor: location module (#2647) refactor: about us module (#2648) refactor: settings module (#2649) refactor: update password module (#2650) --- app/build.gradle.kts | 19 +- .../org/mifos/mobile/MifosSelfServiceApp.kt | 2 +- .../mifos/mobile/ui/about/AboutUsFragment.kt | 65 +-- .../mifos/mobile/ui/about/AboutUsScreen.kt | 74 --- .../mifos/mobile/ui/about/AboutUsViewModel.kt | 61 --- .../mobile/ui/activities/HomeActivity.kt | 12 +- .../SavingsAccountApplicationActivity.kt | 2 +- .../BeneficiaryAddOptionsFragment.kt | 3 +- .../ClientAccountsComposeFragment.kt | 2 +- .../client_accounts/ClientAccountsFragment.kt | 2 +- .../ClientChargeComposeFragment.kt | 4 +- .../org/mifos/mobile/ui/help/HelpFragment.kt | 63 +-- .../mifos/mobile/ui/home/HomeOldFragment.kt | 2 +- .../LoanAccountsDetailFragment.kt | 7 +- .../mobile/ui/location/LocationsFragment.kt | 6 +- .../mifos/mobile/ui/login/LoginActivity.kt | 185 +------ .../ui/notification/NotificationFragment.kt | 1 + .../QrCodeDisplayComposeFragment.kt | 4 +- .../QrCodeImportComposeFragment.kt | 3 +- .../mobile/ui/qr/QrCodeReaderFragment.kt | 1 + .../qr_code_display/QrCodeDisplayFragment.kt | 85 ---- .../ui/qr_code_import/QrCodeImportFragment.kt | 217 -------- .../RecentTransactionsComposeFragment.kt | 1 + .../ui/registration/RegistrationActivity.kt | 7 + .../SavingAccountsDetailFragment.kt | 12 +- .../SavingAccountsTransactionFragment.kt | 4 +- .../SavingsAccountApplicationFragment.kt | 4 +- ...avingsAccountTransactionComposeFragment.kt | 4 +- .../SavingsAccountWithdrawFragment.kt | 4 +- .../SavingsMakeTransferComposeFragment.kt | 6 +- .../SavingsMakeTransferFragment.kt | 432 ---------------- .../ui/settings/SettingsComposeFragment.kt | 1 + .../mobile/ui/settings/SettingsFragment.kt | 196 ------- .../ThirdPartyTransferComposeFragment.kt | 2 + .../ThirdPartyTransferFragment.kt | 478 ------------------ .../TransferProcessComposeFragment.kt | 2 + .../TransferProcessFragment.kt | 231 --------- .../update_password/UpdatePasswordFragment.kt | 20 +- .../ui/user_profile/UserProfileActivity.kt | 2 +- .../UserProfileComposeFragment.kt | 95 ++++ .../ui/user_profile/UserProfileFragment.kt | 2 + .../ui/user_profile/UserProfileScreen.kt | 105 ---- .../ui/widgets/ChargeWidgetDataProvider.kt | 9 +- .../org/mifos/mobile/utils/HelpUiState.kt | 7 - .../org/mifos/mobile/utils/LoginUiState.kt | 9 - .../org/mifos/mobile/utils/StatusUtils.kt | 114 ----- .../mifos/mobile/utils/UserDetailUiState.kt | 12 - .../main/res/layout/fragment_locations.xml | 6 - app/src/main/res/values/strings.xml | 38 -- .../viewModels/AccountsViewModelTest.kt | 31 +- .../viewModels/ClientChargeViewModelTest.kt | 8 +- .../mobile/viewModels/HelpViewModelTest.kt | 2 - .../mobile/viewModels/LoginViewModelTest.kt | 50 +- .../RecentTransactionViewModelTest.kt | 44 +- .../viewModels/UpdatePasswordViewModelTest.kt | 9 +- .../viewModels/UserDetailViewModelTest.kt | 13 +- .../mobile/core/common}/utils/SymbolsUtils.kt | 2 +- .../main/res/drawable/circular_background.xml | 4 +- .../src/main/res/mipmap-hdpi/mifos_icon.png | Bin 0 -> 28552 bytes .../src/main/res/mipmap-mdpi/mifos_icon.png | Bin 0 -> 18888 bytes .../src/main/res/mipmap-xhdpi/mifos_icon.png | Bin 0 -> 38929 bytes .../src/main/res/mipmap-xxhdpi/mifos_icon.png | Bin 0 -> 60911 bytes .../main/res/mipmap-xxxhdpi/mifos_icon.png | Bin 0 -> 84947 bytes core/common/src/main/res/values/dimens.xml | 6 + .../mobile/core/model/entity/AboutUsItem.kt | 1 + feature/about/.gitignore | 1 + feature/about/build.gradle.kts | 17 + feature/about/consumer-rules.pro | 0 feature/about/proguard-rules.pro | 21 + .../feature/about/ExampleInstrumentedTest.kt | 24 + feature/about/src/main/AndroidManifest.xml | 4 + .../mobile/feature/about/ui}/AboutUsHeader.kt | 8 +- .../mobile/feature/about/ui/AboutUsScreen.kt | 97 ++++ .../src/main/res/drawable/ic_law_icon.xml | 0 .../main/res/drawable/ic_privacy_policy.xml | 0 .../src/main/res/drawable/ic_source_code.xml | 2 +- .../src/main/res/drawable/ic_website.xml | 2 +- feature/about/src/main/res/values/strings.xml | 14 + .../mobile/feature/about/ExampleUnitTest.kt | 17 + feature/account/.gitignore | 1 + feature/account/build.gradle.kts | 22 + feature/account/consumer-rules.pro | 0 feature/account/proguard-rules.pro | 21 + feature/account/src/main/AndroidManifest.xml | 4 + .../account/screens}/AccountsScreen.kt | 34 +- .../account/screens}/LoanAccountContent.kt | 7 +- .../account/screens}/SavingsAccountContent.kt | 6 +- .../account/screens}/ShareAccountContent.kt | 7 +- .../utils/AccountTypeItemIndicator.kt | 2 +- .../account}/utils/AccountsFilterUtil.kt | 4 +- .../screens}/ClientAccountsScreen.kt | 19 +- .../utils}/ClientAccountFilterDialog.kt | 4 +- .../utils}/ClientAccountScreenTopBar.kt | 5 +- .../feature/account/utils/AccountState.kt | 14 + .../feature/account/utils/StatusUtils.kt | 127 +++++ .../account/viewmodel}/AccountsViewModel.kt | 54 +- .../main/res/drawable/ic_error_black_24dp.xml | 9 + .../account/src/main/res/values/colors.xml | 74 +++ .../account/src/main/res/values/strings.xml | 37 ++ feature/beneficiary/build.gradle.kts | 2 +- .../BeneficiaryApplicationContent.kt | 2 +- .../BeneficiaryApplicationScreen.kt | 2 +- .../BeneficiaryApplicationViewModel.kt | 2 +- .../BeneficiaryDetailContent.kt | 2 +- .../BeneficiaryDetailScreen.kt | 2 +- .../BeneficiaryDetailViewModel.kt | 2 +- .../beneficiary_list/BeneficiaryListScreen.kt | 2 +- .../presentation/BeneficiaryScreen.kt | 2 +- .../presentation/BeneficiaryScreenIcons.kt | 2 +- .../presentation/RenderIconAndText.kt | 2 +- feature/client_charge/.gitignore | 1 + feature/client_charge/build.gradle.kts | 21 + feature/client_charge/consumer-rules.pro | 0 feature/client_charge/proguard-rules.pro | 21 + .../client_charge/ExampleInstrumentedTest.kt | 24 + .../src/main/AndroidManifest.xml | 4 + .../screens}/ClientChargeScreen.kt | 27 +- .../client_charge/utils/ClientChargeState.kt | 9 + .../viewmodel}/ClientChargeViewModel.kt | 40 +- .../src/main/res/drawable/ic_charges.xml | 46 ++ .../src/main/res/values/strings.xml | 11 + .../feature/client_charge/ExampleUnitTest.kt | 17 + feature/help/.gitignore | 1 + feature/help/build.gradle.kts | 19 + feature/help/consumer-rules.pro | 0 feature/help/proguard-rules.pro | 21 + .../feature/help/ExampleInstrumentedTest.kt | 24 + feature/help/src/main/AndroidManifest.xml | 4 + .../mifos/mobile/feature}/help/HelpScreen.kt | 106 +++- .../mobile/feature}/help/HelpViewModel.kt | 12 +- feature/help/src/main/res/values/strings.xml | 44 ++ .../mobile/feature/help/ExampleUnitTest.kt | 17 + .../LoanRepaymentScheduleViewModel.kt | 1 + .../drawable/ic_check_circle_green_24px.xml | 2 +- feature/loan/src/main/res/values/colors.xml | 74 +++ feature/location/.gitignore | 1 + feature/location/build.gradle.kts | 17 + feature/location/consumer-rules.pro | 0 feature/location/proguard-rules.pro | 21 + .../location/ExampleInstrumentedTest.kt | 24 + feature/location/src/main/AndroidManifest.xml | 4 + .../feature}/location/LocationScreen.kt | 4 +- .../location/src/main/res/values/strings.xml | 7 + .../feature/location/ExampleUnitTest.kt | 17 + feature/login/.gitignore | 1 + feature/login/build.gradle.kts | 19 + feature/login/consumer-rules.pro | 0 feature/login/proguard-rules.pro | 21 + .../feature/login/ExampleInstrumentedTest.kt | 24 + feature/login/src/main/AndroidManifest.xml | 4 + .../feature/login/screens}/LoginScreen.kt | 239 ++++++++- .../mobile/feature/login/utils/LoginState.kt | 9 + .../login/viewmodel}/LoginViewModel.kt | 34 +- .../main/res/drawable/ic_lock_black_24dp.xml | 0 .../res/drawable/ic_person_black_24dp.xml | 9 + .../src/main/res/drawable/mifos_logo.png | Bin 0 -> 60004 bytes feature/login/src/main/res/values/strings.xml | 16 + .../login/src/main/res/values/validations.xml | 5 + .../mobile/feature/login/ExampleUnitTest.kt | 17 + feature/notification/.gitignore | 1 + feature/notification/build.gradle.kts | 23 + feature/notification/consumer-rules.pro | 0 feature/notification/proguard-rules.pro | 21 + .../notification/ExampleInstrumentedTest.kt | 24 + .../notification/src/main/AndroidManifest.xml | 4 + .../notification/NotificationScreen.kt | 3 +- .../notification/NotificationViewModel.kt | 3 +- .../main/res/drawable/ic_notifications.xml | 2 +- .../src/main/res/values/strings.xml | 7 + .../feature/notification/ExampleUnitTest.kt | 17 + feature/qr/.gitignore | 1 + feature/qr/build.gradle.kts | 40 ++ feature/qr/consumer-rules.pro | 0 feature/qr/proguard-rules.pro | 21 + .../feature/qr/ExampleInstrumentedTest.kt | 24 + feature/qr/src/main/AndroidManifest.xml | 4 + .../mobile/feature/qr}/qr/BarcodeCamera.kt | 6 +- .../feature/qr}/qr/QrCodeReaderScreen.kt | 4 +- .../qr_code_display/QrCodeDisplayScreen.kt | 6 +- .../qr_code_display/QrCodeDisplayViewModel.kt | 4 +- .../qr}/qr_code_import/QrCodeImportContent.kt | 4 +- .../qr}/qr_code_import/QrCodeImportScreen.kt | 4 +- .../qr_code_import/QrCodeImportViewModel.kt | 4 +- .../feature/qr}/utils/QrCodeAnalyzer.kt | 2 +- .../feature/qr}/utils/QrCodeGenerator.kt | 2 +- feature/qr/src/main/res/values/strings.xml | 21 + .../mobile/feature/qr/ExampleUnitTest.kt | 17 + feature/recent_transaction/.gitignore | 1 + feature/recent_transaction/build.gradle.kts | 19 + feature/recent_transaction/consumer-rules.pro | 0 feature/recent_transaction/proguard-rules.pro | 21 + .../ExampleInstrumentedTest.kt | 24 + .../src/main/AndroidManifest.xml | 4 + .../screens}/RecentTransactionScreen.kt | 32 +- .../utils/RecentTransactionState.kt | 9 + .../viewmodel}/RecentTransactionViewModel.kt | 24 +- .../main/res/drawable/ic_error_black_24dp.xml | 9 + .../res/drawable/ic_local_atm_black_24dp.xml | 10 + .../src/main/res/values/strings.xml | 8 + .../recent_transaction/ExampleUnitTest.kt | 17 + feature/registration/build.gradle.kts | 2 - .../screens/RegistrationScreen.kt | 33 +- .../screens/RegistrationVerificationScreen.kt | 3 - feature/savings/.gitignore | 1 + feature/savings/build.gradle.kts | 20 + feature/savings/consumer-rules.pro | 0 feature/savings/proguard-rules.pro | 21 + .../savings/ExampleInstrumentedTest.kt | 24 + feature/savings/src/main/AndroidManifest.xml | 4 + .../SavingAccountsDetailViewModel.kt | 4 +- .../SavingsAccountDetailContent.kt | 8 +- .../SavingsAccountDetailScreen.kt | 6 +- .../SavingsAccountDetailTopBar.kt | 5 +- .../SavingsAccountApplicationContent.kt | 4 +- .../SavingsAccountApplicationScreen.kt | 6 +- .../SavingsAccountApplicationViewModel.kt | 4 +- .../SavingAccountTransactionContent.kt | 4 +- .../SavingAccountsTransactionFilterDialog.kt | 8 +- .../SavingAccountsTransactionScreen.kt | 7 +- .../SavingAccountsTransactionViewModel.kt | 18 +- .../SavingsAccountWithdrawScreen.kt | 5 +- .../SavingsAccountWithdrawViewModel.kt | 6 +- .../SavingsMakeTransferContent.kt | 11 +- .../SavingsMakeTransferScreen.kt | 4 +- .../SavingsMakeTransferViewModel.kt | 2 +- .../ic_assignment_turned_in_black_24dp.xml | 10 + .../src/main/res/drawable/ic_charges.xml | 46 ++ .../drawable/ic_compare_arrows_black_24dp.xml | 9 + .../src/main/res/drawable/ic_qrcode_scan.xml | 9 + .../res/drawable/triangular_green_view.xml | 9 + .../main/res/drawable/triangular_red_view.xml | 10 + .../savings/src/main/res/values/strings.xml | 88 ++++ .../mobile/feature/savings/ExampleUnitTest.kt | 17 + feature/settings/.gitignore | 1 + feature/settings/build.gradle.kts | 20 + feature/settings/consumer-rules.pro | 0 feature/settings/proguard-rules.pro | 21 + .../settings/ExampleInstrumentedTest.kt | 24 + feature/settings/src/main/AndroidManifest.xml | 4 + .../feature}/settings/SettingsScreen.kt | 3 +- .../feature}/settings/SettingsViewModel.kt | 29 +- .../feature}/settings/UpdateEndpointDialog.kt | 3 +- .../res/drawable/ic_baseline_dark_mode_24.xml | 10 + .../main/res/drawable/ic_lock_black_24dp.xml | 10 + .../src/main/res/drawable/ic_passcode.xml | 20 +- .../src/main/res/drawable/ic_translate.xml | 20 +- .../src/main/res/drawable/ic_update.xml | 20 +- .../settings/src/main/res/values/strings.xml | 44 ++ .../feature/settings/ExampleUnitTest.kt | 17 + feature/third-party-transfer/.gitignore | 1 + feature/third-party-transfer/build.gradle.kts | 21 + .../third-party-transfer/consumer-rules.pro | 0 .../third-party-transfer/proguard-rules.pro | 21 + .../party/transfer/ExampleInstrumentedTest.kt | 24 + .../src/main/AndroidManifest.xml | 4 + .../ThirdPartyTransferContent.kt | 4 +- .../ThirdPartyTransferScreen.kt | 6 +- .../ThirdPartyTransferViewModel.kt | 13 +- .../src/main/res/values/strings.xml | 31 ++ .../third/party/transfer/ExampleUnitTest.kt | 17 + feature/transfer-process/.gitignore | 1 + feature/transfer-process/build.gradle.kts | 20 + feature/transfer-process/consumer-rules.pro | 0 feature/transfer-process/proguard-rules.pro | 21 + .../process/ExampleInstrumentedTest.kt | 24 + .../src/main/AndroidManifest.xml | 4 + .../process}/TransferProcessScreen.kt | 3 +- .../process}/TransferProcessViewModel.kt | 2 +- .../src/main/res/values/strings.xml | 12 + .../transfer/process/ExampleUnitTest.kt | 17 + feature/update-password/.gitignore | 1 + feature/update-password/build.gradle.kts | 20 + feature/update-password/consumer-rules.pro | 0 feature/update-password/proguard-rules.pro | 21 + .../ExampleInstrumentedTest.kt | 24 + .../src/main/AndroidManifest.xml | 4 + .../update_password/UpdatePasswordContent.kt | 6 +- .../update_password/UpdatePasswordScreen.kt | 29 +- .../UpdatePasswordViewModel.kt | 23 +- .../main/res/drawable/ic_lock_black_24dp.xml | 10 + .../src/main/res/ic_lock_black_24dp.xml | 0 .../src/main/res/values/strings.xml | 18 + .../update_password/ExampleUnitTest.kt | 17 + feature/user_profile/.gitignore | 1 + feature/user_profile/build.gradle.kts | 29 ++ feature/user_profile/consumer-rules.pro | 0 feature/user_profile/proguard-rules.pro | 21 + .../user_profile/ExampleInstrumentedTest.kt | 24 + .../user_profile/src/main/AndroidManifest.xml | 4 + .../screens}/UserProfileDetails.kt | 19 +- .../user_profile/screens/UserProfileScreen.kt | 324 ++++++++++++ .../feature/user_profile/utils/ImageUtil.kt | 136 +++++ .../user_profile/utils/TextDrawable.kt | 281 ++++++++++ .../user_profile/utils/UserDetailUiState.kt | 11 + .../user_profile/utils}/UserDetails.kt | 2 +- .../viewmodel}/UserDetailViewModel.kt | 29 +- .../src/main/res/drawable/ic_cake_24dp.xml | 9 + .../main/res/drawable/ic_error_black_24dp.xml | 9 + .../src/main/res/drawable/ic_gender_24dp.xml | 45 ++ .../ic_keyboard_arrow_right_black_24dp.xml | 9 + .../src/main/res/drawable/ic_phone_24dp.xml | 10 + .../src/main/res/drawable/mifos_logo.png | Bin 0 -> 60004 bytes .../src/main/res/values/colors.xml | 74 +++ .../src/main/res/values/strings.xml | 23 + .../feature/user_profile/ExampleUnitTest.kt | 17 + gradle/libs.versions.toml | 2 + settings.gradle.kts | 14 + .../mobile/core/ui/component/EmptyDataView.kt | 7 +- .../mobile/core/ui/component/MifosIcons.kt | 2 + 309 files changed, 4252 insertions(+), 2882 deletions(-) delete mode 100644 app/src/main/java/org/mifos/mobile/ui/about/AboutUsScreen.kt delete mode 100644 app/src/main/java/org/mifos/mobile/ui/about/AboutUsViewModel.kt rename app/src/main/java/org/mifos/mobile/ui/{qr_code_display => qr}/QrCodeDisplayComposeFragment.kt (89%) rename app/src/main/java/org/mifos/mobile/ui/{qr_code_import => qr}/QrCodeImportComposeFragment.kt (96%) delete mode 100644 app/src/main/java/org/mifos/mobile/ui/qr_code_display/QrCodeDisplayFragment.kt delete mode 100644 app/src/main/java/org/mifos/mobile/ui/qr_code_import/QrCodeImportFragment.kt rename app/src/main/java/org/mifos/mobile/ui/{savings_account_transaction => savings_account}/SavingAccountsTransactionFragment.kt (99%) rename app/src/main/java/org/mifos/mobile/ui/{savings_account_application => savings_account}/SavingsAccountApplicationFragment.kt (92%) rename app/src/main/java/org/mifos/mobile/ui/{savings_account_transaction => savings_account}/SavingsAccountTransactionComposeFragment.kt (87%) rename app/src/main/java/org/mifos/mobile/ui/{savings_account_withdraw => savings_account}/SavingsAccountWithdrawFragment.kt (91%) rename app/src/main/java/org/mifos/mobile/ui/{savings_make_transfer => savings_account}/SavingsMakeTransferComposeFragment.kt (94%) delete mode 100644 app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferFragment.kt delete mode 100644 app/src/main/java/org/mifos/mobile/ui/settings/SettingsFragment.kt delete mode 100644 app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferFragment.kt delete mode 100644 app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessFragment.kt create mode 100644 app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileComposeFragment.kt delete mode 100644 app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileScreen.kt delete mode 100644 app/src/main/java/org/mifos/mobile/utils/LoginUiState.kt delete mode 100644 app/src/main/java/org/mifos/mobile/utils/UserDetailUiState.kt rename {app/src/main/java/org/mifos/mobile => core/common/src/main/java/org/mifos/mobile/core/common}/utils/SymbolsUtils.kt (70%) create mode 100644 core/common/src/main/res/mipmap-hdpi/mifos_icon.png create mode 100644 core/common/src/main/res/mipmap-mdpi/mifos_icon.png create mode 100644 core/common/src/main/res/mipmap-xhdpi/mifos_icon.png create mode 100644 core/common/src/main/res/mipmap-xxhdpi/mifos_icon.png create mode 100644 core/common/src/main/res/mipmap-xxxhdpi/mifos_icon.png create mode 100644 core/common/src/main/res/values/dimens.xml create mode 100644 feature/about/.gitignore create mode 100644 feature/about/build.gradle.kts create mode 100644 feature/about/consumer-rules.pro create mode 100644 feature/about/proguard-rules.pro create mode 100644 feature/about/src/androidTest/java/org/mifos/mobile/feature/about/ExampleInstrumentedTest.kt create mode 100644 feature/about/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui/about => feature/about/src/main/java/org/mifos/mobile/feature/about/ui}/AboutUsHeader.kt (91%) create mode 100644 feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt rename {app => feature/about}/src/main/res/drawable/ic_law_icon.xml (100%) rename {app => feature/about}/src/main/res/drawable/ic_privacy_policy.xml (100%) rename {app => feature/about}/src/main/res/drawable/ic_source_code.xml (91%) rename {app => feature/about}/src/main/res/drawable/ic_website.xml (96%) create mode 100644 feature/about/src/main/res/values/strings.xml create mode 100644 feature/about/src/test/java/org/mifos/mobile/feature/about/ExampleUnitTest.kt create mode 100644 feature/account/.gitignore create mode 100644 feature/account/build.gradle.kts create mode 100644 feature/account/consumer-rules.pro create mode 100644 feature/account/proguard-rules.pro create mode 100644 feature/account/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui/account => feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens}/AccountsScreen.kt (91%) rename {app/src/main/java/org/mifos/mobile/ui/account => feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens}/LoanAccountContent.kt (97%) rename {app/src/main/java/org/mifos/mobile/ui/account => feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens}/SavingsAccountContent.kt (96%) rename {app/src/main/java/org/mifos/mobile/ui/account => feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens}/ShareAccountContent.kt (96%) rename {app/src/main/java/org/mifos/mobile => feature/account/src/main/java/org/mifos/mobile/feature/account/account}/utils/AccountTypeItemIndicator.kt (97%) rename {app/src/main/java/org/mifos/mobile => feature/account/src/main/java/org/mifos/mobile/feature/account/account}/utils/AccountsFilterUtil.kt (92%) rename {app/src/main/java/org/mifos/mobile/ui/client_accounts => feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/screens}/ClientAccountsScreen.kt (91%) rename {app/src/main/java/org/mifos/mobile/ui/client_accounts => feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils}/ClientAccountFilterDialog.kt (98%) rename {app/src/main/java/org/mifos/mobile/ui/client_accounts => feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils}/ClientAccountScreenTopBar.kt (94%) create mode 100644 feature/account/src/main/java/org/mifos/mobile/feature/account/utils/AccountState.kt create mode 100644 feature/account/src/main/java/org/mifos/mobile/feature/account/utils/StatusUtils.kt rename {app/src/main/java/org/mifos/mobile/ui/account => feature/account/src/main/java/org/mifos/mobile/feature/account/viewmodel}/AccountsViewModel.kt (89%) create mode 100644 feature/account/src/main/res/drawable/ic_error_black_24dp.xml create mode 100644 feature/account/src/main/res/values/colors.xml create mode 100644 feature/account/src/main/res/values/strings.xml create mode 100644 feature/client_charge/.gitignore create mode 100644 feature/client_charge/build.gradle.kts create mode 100644 feature/client_charge/consumer-rules.pro create mode 100644 feature/client_charge/proguard-rules.pro create mode 100644 feature/client_charge/src/androidTest/java/org/mifos/mobile/feature/client_charge/ExampleInstrumentedTest.kt create mode 100644 feature/client_charge/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui/client_charge => feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/screens}/ClientChargeScreen.kt (91%) create mode 100644 feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/utils/ClientChargeState.kt rename {app/src/main/java/org/mifos/mobile/ui/client_charge => feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/viewmodel}/ClientChargeViewModel.kt (64%) create mode 100644 feature/client_charge/src/main/res/drawable/ic_charges.xml create mode 100644 feature/client_charge/src/main/res/values/strings.xml create mode 100644 feature/client_charge/src/test/java/org/mifos/mobile/feature/client_charge/ExampleUnitTest.kt create mode 100644 feature/help/.gitignore create mode 100644 feature/help/build.gradle.kts create mode 100644 feature/help/consumer-rules.pro create mode 100644 feature/help/proguard-rules.pro create mode 100644 feature/help/src/androidTest/java/org/mifos/mobile/feature/help/ExampleInstrumentedTest.kt create mode 100644 feature/help/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui => feature/help/src/main/java/org/mifos/mobile/feature}/help/HelpScreen.kt (59%) rename {app/src/main/java/org/mifos/mobile/ui => feature/help/src/main/java/org/mifos/mobile/feature}/help/HelpViewModel.kt (88%) create mode 100644 feature/help/src/main/res/values/strings.xml create mode 100644 feature/help/src/test/java/org/mifos/mobile/feature/help/ExampleUnitTest.kt create mode 100644 feature/loan/src/main/res/values/colors.xml create mode 100644 feature/location/.gitignore create mode 100644 feature/location/build.gradle.kts create mode 100644 feature/location/consumer-rules.pro create mode 100644 feature/location/proguard-rules.pro create mode 100644 feature/location/src/androidTest/java/org/mifos/mobile/feature/location/ExampleInstrumentedTest.kt create mode 100644 feature/location/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui => feature/location/src/main/java/org/mifos/mobile/feature}/location/LocationScreen.kt (95%) create mode 100644 feature/location/src/main/res/values/strings.xml create mode 100644 feature/location/src/test/java/org/mifos/mobile/feature/location/ExampleUnitTest.kt create mode 100644 feature/login/.gitignore create mode 100644 feature/login/build.gradle.kts create mode 100644 feature/login/consumer-rules.pro create mode 100644 feature/login/proguard-rules.pro create mode 100644 feature/login/src/androidTest/java/org/mifos/mobile/feature/login/ExampleInstrumentedTest.kt create mode 100644 feature/login/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui/login => feature/login/src/main/java/org/mifos/mobile/feature/login/screens}/LoginScreen.kt (52%) create mode 100644 feature/login/src/main/java/org/mifos/mobile/feature/login/utils/LoginState.kt rename {app/src/main/java/org/mifos/mobile/ui/login => feature/login/src/main/java/org/mifos/mobile/feature/login/viewmodel}/LoginViewModel.kt (65%) rename {app => feature/login}/src/main/res/drawable/ic_lock_black_24dp.xml (100%) create mode 100644 feature/login/src/main/res/drawable/ic_person_black_24dp.xml create mode 100644 feature/login/src/main/res/drawable/mifos_logo.png create mode 100644 feature/login/src/main/res/values/strings.xml create mode 100644 feature/login/src/main/res/values/validations.xml create mode 100644 feature/login/src/test/java/org/mifos/mobile/feature/login/ExampleUnitTest.kt create mode 100644 feature/notification/.gitignore create mode 100644 feature/notification/build.gradle.kts create mode 100644 feature/notification/consumer-rules.pro create mode 100644 feature/notification/proguard-rules.pro create mode 100644 feature/notification/src/androidTest/java/org/mifos/mobile/feature/notification/ExampleInstrumentedTest.kt create mode 100644 feature/notification/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui => feature/notification/src/main/java/org/mifos/mobile/feature}/notification/NotificationScreen.kt (99%) rename {app/src/main/java/org/mifos/mobile/ui => feature/notification/src/main/java/org/mifos/mobile/feature}/notification/NotificationViewModel.kt (96%) rename {app => feature/notification}/src/main/res/drawable/ic_notifications.xml (91%) create mode 100644 feature/notification/src/main/res/values/strings.xml create mode 100644 feature/notification/src/test/java/org/mifos/mobile/feature/notification/ExampleUnitTest.kt create mode 100644 feature/qr/.gitignore create mode 100644 feature/qr/build.gradle.kts create mode 100644 feature/qr/consumer-rules.pro create mode 100644 feature/qr/proguard-rules.pro create mode 100644 feature/qr/src/androidTest/java/org/mifos/mobile/feature/qr/ExampleInstrumentedTest.kt create mode 100644 feature/qr/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui => feature/qr/src/main/java/org/mifos/mobile/feature/qr}/qr/BarcodeCamera.kt (96%) rename {app/src/main/java/org/mifos/mobile/ui => feature/qr/src/main/java/org/mifos/mobile/feature/qr}/qr/QrCodeReaderScreen.kt (97%) rename {app/src/main/java/org/mifos/mobile/ui => feature/qr/src/main/java/org/mifos/mobile/feature/qr}/qr_code_display/QrCodeDisplayScreen.kt (96%) rename {app/src/main/java/org/mifos/mobile/ui => feature/qr/src/main/java/org/mifos/mobile/feature/qr}/qr_code_display/QrCodeDisplayViewModel.kt (95%) rename {app/src/main/java/org/mifos/mobile/ui => feature/qr/src/main/java/org/mifos/mobile/feature/qr}/qr_code_import/QrCodeImportContent.kt (98%) rename {app/src/main/java/org/mifos/mobile/ui => feature/qr/src/main/java/org/mifos/mobile/feature/qr}/qr_code_import/QrCodeImportScreen.kt (99%) rename {app/src/main/java/org/mifos/mobile/ui => feature/qr/src/main/java/org/mifos/mobile/feature/qr}/qr_code_import/QrCodeImportViewModel.kt (96%) rename {app/src/main/java/org/mifos/mobile => feature/qr/src/main/java/org/mifos/mobile/feature/qr}/utils/QrCodeAnalyzer.kt (97%) rename {app/src/main/java/org/mifos/mobile => feature/qr/src/main/java/org/mifos/mobile/feature/qr}/utils/QrCodeGenerator.kt (98%) create mode 100644 feature/qr/src/main/res/values/strings.xml create mode 100644 feature/qr/src/test/java/org/mifos/mobile/feature/qr/ExampleUnitTest.kt create mode 100644 feature/recent_transaction/.gitignore create mode 100644 feature/recent_transaction/build.gradle.kts create mode 100644 feature/recent_transaction/consumer-rules.pro create mode 100644 feature/recent_transaction/proguard-rules.pro create mode 100644 feature/recent_transaction/src/androidTest/java/org/mifos/mobile/feature/recent_transaction/ExampleInstrumentedTest.kt create mode 100644 feature/recent_transaction/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui/recent_transactions => feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/screens}/RecentTransactionScreen.kt (91%) create mode 100644 feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/utils/RecentTransactionState.kt rename {app/src/main/java/org/mifos/mobile/ui/recent_transactions => feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/viewmodel}/RecentTransactionViewModel.kt (73%) create mode 100644 feature/recent_transaction/src/main/res/drawable/ic_error_black_24dp.xml create mode 100644 feature/recent_transaction/src/main/res/drawable/ic_local_atm_black_24dp.xml create mode 100644 feature/recent_transaction/src/main/res/values/strings.xml create mode 100644 feature/recent_transaction/src/test/java/org/mifos/mobile/feature/recent_transaction/ExampleUnitTest.kt create mode 100644 feature/savings/.gitignore create mode 100644 feature/savings/build.gradle.kts create mode 100644 feature/savings/consumer-rules.pro create mode 100644 feature/savings/proguard-rules.pro create mode 100644 feature/savings/src/androidTest/java/org/mifos/mobile/feature/savings/ExampleInstrumentedTest.kt create mode 100644 feature/savings/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account/SavingAccountsDetailViewModel.kt (96%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account/SavingsAccountDetailContent.kt (98%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account/SavingsAccountDetailScreen.kt (95%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account/SavingsAccountDetailTopBar.kt (96%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account_application/SavingsAccountApplicationContent.kt (98%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account_application/SavingsAccountApplicationScreen.kt (95%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account_application/SavingsAccountApplicationViewModel.kt (98%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account_transaction/SavingAccountTransactionContent.kt (98%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account_transaction/SavingAccountsTransactionFilterDialog.kt (99%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account_transaction/SavingAccountsTransactionScreen.kt (98%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account_transaction/SavingAccountsTransactionViewModel.kt (94%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account_withdraw/SavingsAccountWithdrawScreen.kt (98%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_account_withdraw/SavingsAccountWithdrawViewModel.kt (93%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_make_transfer/SavingsMakeTransferContent.kt (96%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_make_transfer/SavingsMakeTransferScreen.kt (97%) rename {app/src/main/java/org/mifos/mobile/ui => feature/savings/src/main/java/org/mifos/mobile/feature/savings}/savings_make_transfer/SavingsMakeTransferViewModel.kt (98%) create mode 100644 feature/savings/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml create mode 100644 feature/savings/src/main/res/drawable/ic_charges.xml create mode 100644 feature/savings/src/main/res/drawable/ic_compare_arrows_black_24dp.xml create mode 100644 feature/savings/src/main/res/drawable/ic_qrcode_scan.xml create mode 100644 feature/savings/src/main/res/drawable/triangular_green_view.xml create mode 100644 feature/savings/src/main/res/drawable/triangular_red_view.xml create mode 100644 feature/savings/src/main/res/values/strings.xml create mode 100644 feature/savings/src/test/java/org/mifos/mobile/feature/savings/ExampleUnitTest.kt create mode 100644 feature/settings/.gitignore create mode 100644 feature/settings/build.gradle.kts create mode 100644 feature/settings/consumer-rules.pro create mode 100644 feature/settings/proguard-rules.pro create mode 100644 feature/settings/src/androidTest/java/org/mifos/mobile/feature/settings/ExampleInstrumentedTest.kt create mode 100644 feature/settings/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui => feature/settings/src/main/java/org/mifos/mobile/feature}/settings/SettingsScreen.kt (99%) rename {app/src/main/java/org/mifos/mobile/ui => feature/settings/src/main/java/org/mifos/mobile/feature}/settings/SettingsViewModel.kt (79%) rename {app/src/main/java/org/mifos/mobile/ui => feature/settings/src/main/java/org/mifos/mobile/feature}/settings/UpdateEndpointDialog.kt (98%) create mode 100644 feature/settings/src/main/res/drawable/ic_baseline_dark_mode_24.xml create mode 100644 feature/settings/src/main/res/drawable/ic_lock_black_24dp.xml rename {app => feature/settings}/src/main/res/drawable/ic_passcode.xml (98%) rename {app => feature/settings}/src/main/res/drawable/ic_translate.xml (98%) rename {app => feature/settings}/src/main/res/drawable/ic_update.xml (98%) create mode 100644 feature/settings/src/main/res/values/strings.xml create mode 100644 feature/settings/src/test/java/org/mifos/mobile/feature/settings/ExampleUnitTest.kt create mode 100644 feature/third-party-transfer/.gitignore create mode 100644 feature/third-party-transfer/build.gradle.kts create mode 100644 feature/third-party-transfer/consumer-rules.pro create mode 100644 feature/third-party-transfer/proguard-rules.pro create mode 100644 feature/third-party-transfer/src/androidTest/java/org/mifos/mobile/feature/third/party/transfer/ExampleInstrumentedTest.kt create mode 100644 feature/third-party-transfer/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui => feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer}/third_party_transfer/ThirdPartyTransferContent.kt (99%) rename {app/src/main/java/org/mifos/mobile/ui => feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer}/third_party_transfer/ThirdPartyTransferScreen.kt (96%) rename {app/src/main/java/org/mifos/mobile/ui => feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer}/third_party_transfer/ThirdPartyTransferViewModel.kt (88%) create mode 100644 feature/third-party-transfer/src/main/res/values/strings.xml create mode 100644 feature/third-party-transfer/src/test/java/org/mifos/mobile/feature/third/party/transfer/ExampleUnitTest.kt create mode 100644 feature/transfer-process/.gitignore create mode 100644 feature/transfer-process/build.gradle.kts create mode 100644 feature/transfer-process/consumer-rules.pro create mode 100644 feature/transfer-process/proguard-rules.pro create mode 100644 feature/transfer-process/src/androidTest/java/org/mifos/mobile/feature/transfer/process/ExampleInstrumentedTest.kt create mode 100644 feature/transfer-process/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui/transfer_process => feature/transfer-process/src/main/java/org/mifos/mobile/feature/transfer/process}/TransferProcessScreen.kt (99%) rename {app/src/main/java/org/mifos/mobile/ui/transfer_process => feature/transfer-process/src/main/java/org/mifos/mobile/feature/transfer/process}/TransferProcessViewModel.kt (98%) create mode 100644 feature/transfer-process/src/main/res/values/strings.xml create mode 100644 feature/transfer-process/src/test/java/org/mifos/mobile/feature/transfer/process/ExampleUnitTest.kt create mode 100644 feature/update-password/.gitignore create mode 100644 feature/update-password/build.gradle.kts create mode 100644 feature/update-password/consumer-rules.pro create mode 100644 feature/update-password/proguard-rules.pro create mode 100644 feature/update-password/src/androidTest/java/org/mifos/mobile/feature/update_password/ExampleInstrumentedTest.kt create mode 100644 feature/update-password/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui => feature/update-password/src/main/java/org/mifos/mobile/feature}/update_password/UpdatePasswordContent.kt (97%) rename {app/src/main/java/org/mifos/mobile/ui => feature/update-password/src/main/java/org/mifos/mobile/feature}/update_password/UpdatePasswordScreen.kt (88%) rename {app/src/main/java/org/mifos/mobile/ui => feature/update-password/src/main/java/org/mifos/mobile/feature}/update_password/UpdatePasswordViewModel.kt (56%) create mode 100644 feature/update-password/src/main/res/drawable/ic_lock_black_24dp.xml create mode 100644 feature/update-password/src/main/res/ic_lock_black_24dp.xml create mode 100644 feature/update-password/src/main/res/values/strings.xml create mode 100644 feature/update-password/src/test/java/org/mifos/mobile/feature/update_password/ExampleUnitTest.kt create mode 100644 feature/user_profile/.gitignore create mode 100644 feature/user_profile/build.gradle.kts create mode 100644 feature/user_profile/consumer-rules.pro create mode 100644 feature/user_profile/proguard-rules.pro create mode 100644 feature/user_profile/src/androidTest/java/org/mifos/mobile/feature/user_profile/ExampleInstrumentedTest.kt create mode 100644 feature/user_profile/src/main/AndroidManifest.xml rename {app/src/main/java/org/mifos/mobile/ui/user_profile => feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/screens}/UserProfileDetails.kt (86%) create mode 100644 feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/screens/UserProfileScreen.kt create mode 100644 feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/ImageUtil.kt create mode 100644 feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/TextDrawable.kt create mode 100644 feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/UserDetailUiState.kt rename {app/src/main/java/org/mifos/mobile/ui/user_profile => feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils}/UserDetails.kt (92%) rename {app/src/main/java/org/mifos/mobile/ui/user_profile => feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/viewmodel}/UserDetailViewModel.kt (78%) create mode 100644 feature/user_profile/src/main/res/drawable/ic_cake_24dp.xml create mode 100644 feature/user_profile/src/main/res/drawable/ic_error_black_24dp.xml create mode 100644 feature/user_profile/src/main/res/drawable/ic_gender_24dp.xml create mode 100644 feature/user_profile/src/main/res/drawable/ic_keyboard_arrow_right_black_24dp.xml create mode 100644 feature/user_profile/src/main/res/drawable/ic_phone_24dp.xml create mode 100644 feature/user_profile/src/main/res/drawable/mifos_logo.png create mode 100644 feature/user_profile/src/main/res/values/colors.xml create mode 100644 feature/user_profile/src/main/res/values/strings.xml create mode 100644 feature/user_profile/src/test/java/org/mifos/mobile/feature/user_profile/ExampleUnitTest.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f1ff92fe60..09a5ce39a9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -76,10 +76,25 @@ dependencies { implementation(projects.core.data) implementation(projects.core.datastore) implementation(projects.ui) + implementation(projects.feature.loan) + implementation(projects.feature.login) implementation(projects.feature.registration) implementation(projects.feature.beneficiary) implementation(projects.feature.guarantor) + implementation(projects.feature.savings) + implementation(projects.feature.qr) + implementation(projects.feature.transferProcess) + implementation(projects.feature.account) + implementation(projects.feature.clientCharge) + implementation(projects.feature.recentTransaction) + implementation(projects.feature.thirdPartyTransfer) + implementation(projects.feature.help) + implementation(projects.feature.notification) + implementation(projects.feature.location) + implementation(projects.feature.about) + implementation(projects.feature.settings) + implementation(projects.feature.updatePassword) implementation("androidx.legacy:legacy-support-v4:1.0.0") @@ -88,7 +103,7 @@ dependencies { implementation(libs.androidx.appcompat) implementation(libs.material) implementation(libs.androidx.preference) - implementation(libs.play.services.maps) + // DBFlow implementation(libs.dbflow) @@ -186,8 +201,6 @@ dependencies { debugApi(libs.androidx.compose.ui.tooling) api(libs.androidx.hilt.navigation.compose) - // google maps - implementation ("com.google.maps.android:maps-compose:4.4.1") //image cropper implementation("com.github.CanHub:Android-Image-Cropper:4.0.0") diff --git a/app/src/main/java/org/mifos/mobile/MifosSelfServiceApp.kt b/app/src/main/java/org/mifos/mobile/MifosSelfServiceApp.kt index bebd021777..9dc985bb0f 100644 --- a/app/src/main/java/org/mifos/mobile/MifosSelfServiceApp.kt +++ b/app/src/main/java/org/mifos/mobile/MifosSelfServiceApp.kt @@ -11,8 +11,8 @@ import com.raizlabs.android.dbflow.config.FlowConfig import com.raizlabs.android.dbflow.config.FlowManager import dagger.hilt.android.HiltAndroidApp import org.mifos.mobile.core.datastore.PreferencesHelper -import org.mifos.mobile.ui.settings.applySavedTheme import org.mifos.mobile.core.common.utils.LanguageHelper.onAttach +import org.mifos.mobile.feature.settings.applySavedTheme /** * @author ishan diff --git a/app/src/main/java/org/mifos/mobile/ui/about/AboutUsFragment.kt b/app/src/main/java/org/mifos/mobile/ui/about/AboutUsFragment.kt index c938dea15d..a802b07f62 100644 --- a/app/src/main/java/org/mifos/mobile/ui/about/AboutUsFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/about/AboutUsFragment.kt @@ -8,70 +8,59 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy -import androidx.fragment.app.viewModels import com.google.android.gms.oss.licenses.OssLicensesMenuActivity import dagger.hilt.android.AndroidEntryPoint -import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.ui.activities.PrivacyPolicyActivity -import org.mifos.mobile.core.model.enums.AboutUsListItemId import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.core.common.Constants.LICENSE_LINK import org.mifos.mobile.core.common.Constants.SOURCE_CODE_LINK import org.mifos.mobile.core.common.Constants.WEBSITE_LINK +import org.mifos.mobile.core.model.enums.AboutUsListItemId +import org.mifos.mobile.core.ui.component.mifosComposeView +import org.mifos.mobile.feature.about.ui.AboutUsScreen + -/* -~This project is licensed under the open source MPL V2. -~See https://github.com/openMF/self-service-app/blob/master/LICENSE.md -*/ @AndroidEntryPoint class AboutUsFragment : BaseFragment() { - private val viewModel: AboutUsViewModel by viewModels() - @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { - return ComposeView(requireContext()).apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - MifosMobileTheme { - AboutUsScreen(viewModel) + return mifosComposeView(requireContext()) { + AboutUsScreen( + navigateToItem = { + navigateToItem(it.itemId) } - } + ) } } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewModel.aboutUsItemEvent.observe(viewLifecycleOwner) { - when (it) { - AboutUsListItemId.OFFICE_WEBSITE -> { - startActivity(WEBSITE_LINK) - } - - AboutUsListItemId.LICENSES -> { - startActivity(LICENSE_LINK) - } + private fun navigateToItem(aboutUsItem: AboutUsListItemId) { + when(aboutUsItem) { + AboutUsListItemId.OFFICE_WEBSITE -> { + startActivity(WEBSITE_LINK) + } - AboutUsListItemId.PRIVACY_POLICY -> { - startActivity(PrivacyPolicyActivity::class.java) - } + AboutUsListItemId.LICENSES -> { + startActivity(LICENSE_LINK) + } - AboutUsListItemId.SOURCE_CODE -> { - startActivity(SOURCE_CODE_LINK) - } + AboutUsListItemId.PRIVACY_POLICY -> { + startActivity(PrivacyPolicyActivity::class.java) + } - AboutUsListItemId.LICENSES_STRING_WITH_VALUE -> { - startActivity(OssLicensesMenuActivity::class.java) - } + AboutUsListItemId.SOURCE_CODE -> { + startActivity(SOURCE_CODE_LINK) + } - else -> {} + AboutUsListItemId.LICENSES_STRING_WITH_VALUE -> { + startActivity(OssLicensesMenuActivity::class.java) } + + AboutUsListItemId.APP_VERSION_TEXT -> Unit } } diff --git a/app/src/main/java/org/mifos/mobile/ui/about/AboutUsScreen.kt b/app/src/main/java/org/mifos/mobile/ui/about/AboutUsScreen.kt deleted file mode 100644 index 3bad22efc1..0000000000 --- a/app/src/main/java/org/mifos/mobile/ui/about/AboutUsScreen.kt +++ /dev/null @@ -1,74 +0,0 @@ -package org.mifos.mobile.ui.about - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import org.mifos.mobile.core.model.enums.AboutUsListItemId -import org.mifos.mobile.core.ui.component.AboutUsItemCard -import org.mifos.mobile.core.ui.component.MifosItemCard -import org.mifos.mobile.core.ui.theme.MifosMobileTheme - -@Composable -fun AboutUsScreen(viewModel: AboutUsViewModel) { - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - ) { - item { - Spacer(modifier = Modifier.height(48.dp)) - AboutUsHeader() - } - items(viewModel.aboutUsItems) { item -> - MifosItemCard( - modifier = Modifier.padding(bottom = 8.dp), - onClick = { - when (item.itemId) { - AboutUsListItemId.OFFICE_WEBSITE -> { - viewModel.navigateToItem(AboutUsListItemId.OFFICE_WEBSITE) - } - - AboutUsListItemId.LICENSES -> { - viewModel.navigateToItem(AboutUsListItemId.LICENSES) - } - - AboutUsListItemId.PRIVACY_POLICY -> { - viewModel.navigateToItem(AboutUsListItemId.PRIVACY_POLICY) - } - - AboutUsListItemId.SOURCE_CODE -> { - viewModel.navigateToItem(AboutUsListItemId.SOURCE_CODE) - } - - AboutUsListItemId.LICENSES_STRING_WITH_VALUE -> { - viewModel.navigateToItem(AboutUsListItemId.LICENSES_STRING_WITH_VALUE) - } - - else -> {} - } - } - ) { - item.title?.let { - AboutUsItemCard( - title = it, - subtitle = item.subtitle, - iconUrl = item.iconUrl - ) - } - } - } - } - -} - -@Preview(showSystemUi = true, device = "id:pixel_5") -@Composable -fun AboutScreenPreview() { - MifosMobileTheme { - AboutUsScreen(AboutUsViewModel()) - } -} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/about/AboutUsViewModel.kt b/app/src/main/java/org/mifos/mobile/ui/about/AboutUsViewModel.kt deleted file mode 100644 index f7d8d70b93..0000000000 --- a/app/src/main/java/org/mifos/mobile/ui/about/AboutUsViewModel.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.mifos.mobile.ui.about - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import dagger.hilt.android.lifecycle.HiltViewModel -import org.mifos.mobile.MifosSelfServiceApp.Companion.context -import org.mifos.mobile.R -import org.mifos.mobile.core.model.entity.AboutUsItem -import org.mifos.mobile.core.model.enums.AboutUsListItemId -import java.util.* -import javax.inject.Inject - -@HiltViewModel -class AboutUsViewModel @Inject constructor() : ViewModel() { - - private val _aboutUsItemEvent = MutableLiveData() - val aboutUsItemEvent: LiveData get() = _aboutUsItemEvent - - private val currentYear = Calendar.getInstance().get(Calendar.YEAR) - private val copyrightText = - context?.getString(R.string.copy_right_mifos) - ?.replace("%1\$s", currentYear.toString()) - - val aboutUsItems = listOf( - AboutUsItem( - title = context?.getString(R.string.app_version_text), - itemId = AboutUsListItemId.APP_VERSION_TEXT - ), - AboutUsItem( - title = context?.getString(R.string.official_website), - iconUrl = R.drawable.ic_website, - itemId = AboutUsListItemId.OFFICE_WEBSITE - ), - AboutUsItem( - title = context?.getString(R.string.licenses), - iconUrl = R.drawable.ic_law_icon, - itemId = AboutUsListItemId.LICENSES - ), - AboutUsItem( - title = context?.getString(R.string.privacy_policy), - iconUrl = R.drawable.ic_privacy_policy, - itemId = AboutUsListItemId.PRIVACY_POLICY - ), - AboutUsItem( - title = context?.getString(R.string.sources), - iconUrl = R.drawable.ic_source_code, - itemId = AboutUsListItemId.SOURCE_CODE - ), - AboutUsItem( - title = copyrightText, - subtitle = R.string.license_string_with_value, - itemId = AboutUsListItemId.LICENSES_STRING_WITH_VALUE - ) - ) - - fun navigateToItem(itemId: AboutUsListItemId) { - _aboutUsItemEvent.value = itemId - } - -} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/activities/HomeActivity.kt b/app/src/main/java/org/mifos/mobile/ui/activities/HomeActivity.kt index db918d3fc7..c1cebacc9e 100644 --- a/app/src/main/java/org/mifos/mobile/ui/activities/HomeActivity.kt +++ b/app/src/main/java/org/mifos/mobile/ui/activities/HomeActivity.kt @@ -47,11 +47,11 @@ import org.mifos.mobile.core.datastore.PreferencesHelper import org.mifos.mobile.core.model.entity.client.Client import org.mifos.mobile.utils.TextDrawable import org.mifos.mobile.utils.Toaster -import org.mifos.mobile.utils.UserDetailUiState +import org.mifos.mobile.feature.user_profile.utils.UserDetailState import org.mifos.mobile.utils.fcm.RegistrationIntentService -import org.mifos.mobile.ui.user_profile.UserDetailViewModel import org.mifos.mobile.ui.user_profile.UserProfileActivity import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedParcelable +import org.mifos.mobile.feature.user_profile.viewmodel.UserDetailViewModel import javax.inject.Inject import org.mifos.mobile.ui.settings.SettingsActivity @@ -117,16 +117,16 @@ class HomeActivity : lifecycleScope.launchWhenStarted { viewModel.userDetailUiState.collect { when (it) { - is UserDetailUiState.Loading -> showProgress() - is UserDetailUiState.ShowUserDetails -> { + is UserDetailState.Loading -> showProgress() + is UserDetailState.ShowUserDetails -> { hideProgress() showUserDetails(it.client) } - is UserDetailUiState.ShowUserImage -> { + is UserDetailState.ShowUserImage -> { hideProgress() showUserImage(it.image) } - is UserDetailUiState.ShowError -> { + is UserDetailState.ShowError -> { hideProgress() showError(getString(it.message)) } diff --git a/app/src/main/java/org/mifos/mobile/ui/activities/SavingsAccountApplicationActivity.kt b/app/src/main/java/org/mifos/mobile/ui/activities/SavingsAccountApplicationActivity.kt index 391b290277..5b7051de85 100644 --- a/app/src/main/java/org/mifos/mobile/ui/activities/SavingsAccountApplicationActivity.kt +++ b/app/src/main/java/org/mifos/mobile/ui/activities/SavingsAccountApplicationActivity.kt @@ -5,7 +5,7 @@ import org.mifos.mobile.R import org.mifos.mobile.databinding.ActivitySavingsAccountApplicationBinding import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.core.model.enums.SavingsAccountState -import org.mifos.mobile.ui.savings_account_application.SavingsAccountApplicationFragment.Companion.newInstance +import org.mifos.mobile.ui.savings_account.SavingsAccountApplicationFragment.Companion.newInstance /* * Created by saksham on 30/June/2018 diff --git a/app/src/main/java/org/mifos/mobile/ui/beneficiary/presentation/BeneficiaryAddOptionsFragment.kt b/app/src/main/java/org/mifos/mobile/ui/beneficiary/presentation/BeneficiaryAddOptionsFragment.kt index d5a9e64a25..6fbe4ee82b 100644 --- a/app/src/main/java/org/mifos/mobile/ui/beneficiary/presentation/BeneficiaryAddOptionsFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/beneficiary/presentation/BeneficiaryAddOptionsFragment.kt @@ -15,12 +15,11 @@ import org.mifos.mobile.R import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.databinding.FragmentBeneficiaryAddOptionsBinding import org.mifos.mobile.ui.activities.base.BaseActivity -import org.mifos.mobile.ui.beneficiary_application.BeneficiaryApplicationComposeFragment import org.mifos.mobile.core.model.enums.BeneficiaryState import org.mifos.mobile.core.model.enums.RequestAccessType import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.ui.qr.QrCodeReaderFragment -import org.mifos.mobile.ui.qr_code_import.QrCodeImportComposeFragment +import org.mifos.mobile.ui.qr.QrCodeImportComposeFragment import org.mifos.mobile.utils.CheckSelfPermissionAndRequest import org.mifos.mobile.utils.CheckSelfPermissionAndRequest.checkSelfPermission import org.mifos.mobile.utils.CheckSelfPermissionAndRequest.requestPermission diff --git a/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsComposeFragment.kt index 65ce2c0701..1af99ce4f5 100644 --- a/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsComposeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsComposeFragment.kt @@ -1,6 +1,5 @@ package org.mifos.mobile.ui.client_accounts - import android.content.Intent import android.os.Bundle import android.view.LayoutInflater @@ -16,6 +15,7 @@ import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.core.model.enums.AccountType import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.core.common.Constants +import org.mifos.mobile.feature.account.client_account.screens.ClientAccountsScreen /* ~This project is licensed under the open source MPL V2. diff --git a/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsFragment.kt b/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsFragment.kt index 897c094305..1b39f5917d 100644 --- a/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsFragment.kt @@ -34,7 +34,7 @@ import org.mifos.mobile.ui.enums.AccountType import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.core.common.Constants import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedSerializable -import org.mifos.mobile.utils.StatusUtils +import org.mifos.mobile.feature.account.utils.StatusUtils import javax.inject.Inject /* diff --git a/app/src/main/java/org/mifos/mobile/ui/client_charge/ClientChargeComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/client_charge/ClientChargeComposeFragment.kt index 83c13ae5d0..27e8e20360 100644 --- a/app/src/main/java/org/mifos/mobile/ui/client_charge/ClientChargeComposeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/client_charge/ClientChargeComposeFragment.kt @@ -12,6 +12,8 @@ import org.mifos.mobile.core.model.enums.ChargeType import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.core.common.Constants import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedSerializable +import org.mifos.mobile.feature.client_charge.screens.ClientChargeScreen +import org.mifos.mobile.feature.client_charge.viewmodel.ClientChargeViewModel /** * @author Vishwajeet @@ -39,7 +41,7 @@ class ClientChargeComposeFragment : BaseFragment() { savedInstanceState: Bundle?, ): View { return mifosComposeView(requireContext()) { - ClientChargeScreen ( + ClientChargeScreen( navigateBack = { activity?.onBackPressed() } ) } diff --git a/app/src/main/java/org/mifos/mobile/ui/help/HelpFragment.kt b/app/src/main/java/org/mifos/mobile/ui/help/HelpFragment.kt index 1d9faa27bf..4b567be66d 100644 --- a/app/src/main/java/org/mifos/mobile/ui/help/HelpFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/help/HelpFragment.kt @@ -7,23 +7,18 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch import org.mifos.mobile.R -import org.mifos.mobile.core.model.entity.FAQ +import org.mifos.mobile.core.ui.component.mifosComposeView import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.feature.help.HelpScreen +import org.mifos.mobile.feature.help.HelpViewModel import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.ui.location.LocationsFragment -import org.mifos.mobile.utils.HelpUiState /* ~This project is licensed under the open source MPL V2. @@ -32,59 +27,19 @@ import org.mifos.mobile.utils.HelpUiState @AndroidEntryPoint class HelpFragment : BaseFragment() { - private val viewModel: HelpViewModel by viewModels() - private var faqArrayList: MutableState> = mutableStateOf(arrayListOf()) - private var selectedFaqPosition: MutableState = mutableStateOf(-1) override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { - if (savedInstanceState == null) { - viewModel.loadFaq( - context?.resources?.getStringArray(R.array.faq_qs), - context?.resources?.getStringArray(R.array.faq_ans) + return mifosComposeView(requireContext()) { + HelpScreen( + callNow = { callHelpline() }, + leaveEmail = { mailHelpline() }, + findLocations = { findLocations() }, + navigateBack = { activity?.finish() }, ) } - return ComposeView(requireContext()).apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - MifosMobileTheme { - HelpScreen( - faqArrayList = faqArrayList.value, - callNow = { callHelpline() }, - leaveEmail = { mailHelpline() }, - findLocations = { findLocations() }, - navigateBack = { activity?.finish() }, - onSearchDismiss = { viewModel.loadFaq(qs = null, ans = null) }, - searchQuery = { viewModel.filterList(it) }, - selectedFaqPosition = selectedFaqPosition.value, - updateFaqPosition = { viewModel.updateSelectedFaqPosition(it) } - ) - } - } - } - } - - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.helpUiState.collect() { - when (it) { - - is HelpUiState.ShowFaq -> { - faqArrayList.value = it.faqArrayList - selectedFaqPosition.value = it.selectedFaqPosition - } - - HelpUiState.Initial -> {} - } - } - } - } } private fun callHelpline() { diff --git a/app/src/main/java/org/mifos/mobile/ui/home/HomeOldFragment.kt b/app/src/main/java/org/mifos/mobile/ui/home/HomeOldFragment.kt index f3572a22ae..4aef6b338c 100644 --- a/app/src/main/java/org/mifos/mobile/ui/home/HomeOldFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/home/HomeOldFragment.kt @@ -33,7 +33,7 @@ import org.mifos.mobile.ui.client_charge.ClientChargeComposeFragment import org.mifos.mobile.core.model.enums.AccountType import org.mifos.mobile.core.model.enums.ChargeType import org.mifos.mobile.ui.fragments.base.BaseFragment -import org.mifos.mobile.ui.savings_make_transfer.SavingsMakeTransferComposeFragment +import org.mifos.mobile.ui.savings_account.SavingsMakeTransferComposeFragment import org.mifos.mobile.ui.third_party_transfer.ThirdPartyTransferComposeFragment import org.mifos.mobile.ui.user_profile.UserProfileActivity import org.mifos.mobile.core.datastore.PreferencesHelper diff --git a/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailFragment.kt b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailFragment.kt index 916eac6517..dc42cdcece 100644 --- a/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/loan_account/LoanAccountsDetailFragment.kt @@ -25,11 +25,10 @@ import org.mifos.mobile.ui.loan_account_transaction.LoanAccountTransactionFragme import org.mifos.mobile.ui.loan_account_withdraw.LoanAccountWithdrawFragment import org.mifos.mobile.ui.loan_account_application.LoanApplicationFragment import org.mifos.mobile.ui.loan_repayment_schedule.LoanRepaymentScheduleFragment -import org.mifos.mobile.ui.savings_make_transfer.SavingsMakeTransferFragment import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.ui.loan_account_summary.LoanAccountSummaryFragment -import org.mifos.mobile.ui.qr_code_display.QrCodeDisplayComposeFragment -import org.mifos.mobile.ui.savings_make_transfer.SavingsMakeTransferComposeFragment +import org.mifos.mobile.ui.qr.QrCodeDisplayComposeFragment +import org.mifos.mobile.ui.savings_account.SavingsMakeTransferComposeFragment import org.mifos.mobile.utils.* import javax.inject.Inject @@ -154,7 +153,7 @@ class LoanAccountsDetailFragment : BaseFragment() { } private fun onQrCodeClicked() { - val accountDetailsInJson = QrCodeGenerator.getAccountDetailsInString( + val accountDetailsInJson = org.mifos.mobile.feature.qr.utils.QrCodeGenerator.getAccountDetailsInString( viewModel.loanWithAssociations?.accountNo, preferencesHelper.officeName, AccountType.LOAN, diff --git a/app/src/main/java/org/mifos/mobile/ui/location/LocationsFragment.kt b/app/src/main/java/org/mifos/mobile/ui/location/LocationsFragment.kt index e0edb381b4..cbcf460f74 100644 --- a/app/src/main/java/org/mifos/mobile/ui/location/LocationsFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/location/LocationsFragment.kt @@ -6,7 +6,7 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import org.mifos.mobile.core.ui.component.mifosComposeView -import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.feature.location.LocationsScreen class LocationsFragment : Fragment() { @@ -16,9 +16,7 @@ class LocationsFragment : Fragment() { savedInstanceState: Bundle? ): View { return mifosComposeView(requireContext()) { - MifosMobileTheme { - LocationsScreen() - } + LocationsScreen() } } diff --git a/app/src/main/java/org/mifos/mobile/ui/login/LoginActivity.kt b/app/src/main/java/org/mifos/mobile/ui/login/LoginActivity.kt index 346951d249..a4531139bf 100644 --- a/app/src/main/java/org/mifos/mobile/ui/login/LoginActivity.kt +++ b/app/src/main/java/org/mifos/mobile/ui/login/LoginActivity.kt @@ -2,22 +2,13 @@ package org.mifos.mobile.ui.login import android.content.Intent import android.os.Bundle -import android.widget.Toast import androidx.activity.compose.setContent -import androidx.activity.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch -import org.mifos.mobile.MifosSelfServiceApp.Companion.context -import org.mifos.mobile.R import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.feature.login.screens.LoginScreen import org.mifos.mobile.ui.activities.PassCodeActivity import org.mifos.mobile.ui.registration.RegistrationActivity import org.mifos.mobile.ui.activities.base.BaseActivity -import org.mifos.mobile.utils.LoginUiState -import org.mifos.mobile.core.common.Network /** * @author Vishwajeet @@ -26,189 +17,22 @@ import org.mifos.mobile.core.common.Network @AndroidEntryPoint class LoginActivity : BaseActivity() { - private val viewModel: LoginViewModel by viewModels() - - private lateinit var usernameContent: String - private lateinit var passwordContent: String - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MifosMobileTheme { + LoginScreen( - login = { username, password -> - usernameContent = username - passwordContent = password - onLoginClicked() - }, - createAccount = { onRegisterClicked() }, - getUsernameError = { validateUsername(it) }, - getPasswordError = { validatePassword(it) } + startRegisterActivity = { onRegisterClicked() }, + startPassCodeActivity = { startPassCodeActivity() }, ) } } - - lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.loginUiState.collect { state -> - when (state) { - LoginUiState.Loading -> showProgress() - - LoginUiState.Error -> { - hideProgress() - showMessage(context?.getString(R.string.login_failed)) - } - - LoginUiState.LoginSuccess -> { - onLoginSuccess() - } - - is LoginUiState.LoadClientSuccess -> { - hideProgress() - showPassCodeActivity(state.clientName) - } - - LoginUiState.Initial -> {} - } - } - } - } - } - - /** - * Called when Login is user has successfully logged in - */ - private fun onLoginSuccess() { - viewModel.loadClient() - } - - /** - * Shows ProgressDialog when called - */ - private fun showProgress() { - showProgressDialog(getString(R.string.progress_message_login)) - } - - /** - * Hides the progressDialog which is being shown - */ - private fun hideProgress() { - hideProgressDialog() - } - - /** - * Starts [PassCodeActivity] - */ - private fun showPassCodeActivity(clientName: String?) { - showToast(getString(R.string.toast_welcome, clientName)) - startPassCodeActivity() - } - - /** - * It is called whenever any error occurs while executing a request - * - * @param errorMessage Error message that tells the user about the problem. - */ - private fun showMessage(errorMessage: String?) { - showToast(errorMessage!!, Toast.LENGTH_LONG) - } - - /** - * Called when Login Button is clicked, used for logging in the user - */ - - private fun onLoginClicked() { - if (Network.isConnected(this)) { - if (isCredentialsValid(usernameContent, passwordContent)) - viewModel.login(usernameContent, passwordContent) - } else { - showMessage(context?.getString(R.string.no_internet_connection)) - } - } - - private fun isCredentialsValid(username: String, password: String): Boolean { - var credentialValid = true - when { - viewModel.isFieldEmpty(username) -> { - credentialValid = false - } - - viewModel.isUsernameLengthInadequate(username) -> { - credentialValid = false - } - - viewModel.usernameHasSpaces(username) -> { - credentialValid = false - } - } - - when { - viewModel.isFieldEmpty(password) -> { - credentialValid = false - } - - viewModel.isPasswordLengthInadequate(password) -> { - credentialValid = false - } - } - return credentialValid - } - - private fun validateUsername(username: String): String { - var usernameError = "" - when { - viewModel.isFieldEmpty(username) -> { - usernameError = context?.getString( - R.string.error_validation_blank, - context?.getString(R.string.username), - ).toString() - } - - viewModel.isUsernameLengthInadequate(username) -> { - usernameError = context?.getString( - R.string.error_validation_minimum_chars, - resources?.getString(R.string.username), - resources?.getInteger(R.integer.username_minimum_length), - ).toString() - } - - viewModel.usernameHasSpaces(username) -> { - usernameError = context?.getString( - R.string.error_validation_cannot_contain_spaces, - resources?.getString(R.string.username), - context?.getString(R.string.not_contain_username), - ).toString() - } - } - return usernameError - } - - private fun validatePassword(password: String): String { - var passwordError = "" - when { - viewModel.isFieldEmpty(password) -> { - passwordError = context?.getString( - R.string.error_validation_blank, - context?.getString(R.string.password), - ).toString() - - } - - viewModel.isPasswordLengthInadequate(password) -> { - passwordError = context?.getString( - R.string.error_validation_minimum_chars, - resources?.getString(R.string.password), - resources?.getInteger(R.integer.password_minimum_length), - ).toString() - } - } - return passwordError } private fun onRegisterClicked() { startActivity(Intent(this@LoginActivity, RegistrationActivity::class.java)) } - /** * Starts [PassCodeActivity] with `Constans.INTIAL_LOGIN` as true */ @@ -219,4 +43,5 @@ class LoginActivity : BaseActivity() { startActivity(intent) finish() } + } diff --git a/app/src/main/java/org/mifos/mobile/ui/notification/NotificationFragment.kt b/app/src/main/java/org/mifos/mobile/ui/notification/NotificationFragment.kt index 002d6bd3c3..651b146c3c 100644 --- a/app/src/main/java/org/mifos/mobile/ui/notification/NotificationFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/notification/NotificationFragment.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import dagger.hilt.android.AndroidEntryPoint import org.mifos.mobile.core.ui.component.mifosComposeView +import org.mifos.mobile.feature.notification.NotificationScreen import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.fragments.base.BaseFragment diff --git a/app/src/main/java/org/mifos/mobile/ui/qr_code_display/QrCodeDisplayComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/qr/QrCodeDisplayComposeFragment.kt similarity index 89% rename from app/src/main/java/org/mifos/mobile/ui/qr_code_display/QrCodeDisplayComposeFragment.kt rename to app/src/main/java/org/mifos/mobile/ui/qr/QrCodeDisplayComposeFragment.kt index 4b101352b6..65ff713cdc 100644 --- a/app/src/main/java/org/mifos/mobile/ui/qr_code_display/QrCodeDisplayComposeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/qr/QrCodeDisplayComposeFragment.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.qr_code_display +package org.mifos.mobile.ui.qr import android.os.Bundle import android.view.LayoutInflater @@ -10,6 +10,8 @@ import org.mifos.mobile.core.ui.component.mifosComposeView import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.core.common.Constants +import org.mifos.mobile.feature.qr.qr_code_display.QrCodeDisplayScreen +import org.mifos.mobile.feature.qr.qr_code_display.QrCodeDisplayViewModel @AndroidEntryPoint class QrCodeDisplayComposeFragment : BaseFragment() { diff --git a/app/src/main/java/org/mifos/mobile/ui/qr_code_import/QrCodeImportComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/qr/QrCodeImportComposeFragment.kt similarity index 96% rename from app/src/main/java/org/mifos/mobile/ui/qr_code_import/QrCodeImportComposeFragment.kt rename to app/src/main/java/org/mifos/mobile/ui/qr/QrCodeImportComposeFragment.kt index 26fadfc9f9..99e575b1bd 100644 --- a/app/src/main/java/org/mifos/mobile/ui/qr_code_import/QrCodeImportComposeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/qr/QrCodeImportComposeFragment.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.qr_code_import +package org.mifos.mobile.ui.qr import android.os.Build import android.os.Bundle @@ -19,6 +19,7 @@ import org.mifos.mobile.core.model.enums.BeneficiaryState import org.mifos.mobile.ui.fragments.base.BaseFragment import com.google.zxing.Result import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary +import org.mifos.mobile.feature.qr.qr_code_import.QrCodeImportScreen @AndroidEntryPoint class QrCodeImportComposeFragment : BaseFragment() { diff --git a/app/src/main/java/org/mifos/mobile/ui/qr/QrCodeReaderFragment.kt b/app/src/main/java/org/mifos/mobile/ui/qr/QrCodeReaderFragment.kt index db76a46203..2de4983312 100644 --- a/app/src/main/java/org/mifos/mobile/ui/qr/QrCodeReaderFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/qr/QrCodeReaderFragment.kt @@ -13,6 +13,7 @@ import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary import org.mifos.mobile.core.ui.component.mifosComposeView import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.core.model.enums.BeneficiaryState +import org.mifos.mobile.feature.qr.qr.QrCodeReaderScreen import org.mifos.mobile.ui.beneficiary_application.BeneficiaryApplicationComposeFragment import org.mifos.mobile.ui.fragments.base.BaseFragment diff --git a/app/src/main/java/org/mifos/mobile/ui/qr_code_display/QrCodeDisplayFragment.kt b/app/src/main/java/org/mifos/mobile/ui/qr_code_display/QrCodeDisplayFragment.kt deleted file mode 100644 index 15f086deb7..0000000000 --- a/app/src/main/java/org/mifos/mobile/ui/qr_code_display/QrCodeDisplayFragment.kt +++ /dev/null @@ -1,85 +0,0 @@ -package org.mifos.mobile.ui.qr_code_display - -import android.content.Intent -import android.graphics.drawable.BitmapDrawable -import android.os.Bundle -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import dagger.hilt.android.AndroidEntryPoint -import org.mifos.mobile.R -import org.mifos.mobile.databinding.FragmentQrCodeDisplayBinding -import org.mifos.mobile.ui.activities.base.BaseActivity -import org.mifos.mobile.ui.fragments.base.BaseFragment -import org.mifos.mobile.core.common.Constants -import org.mifos.mobile.utils.QrCodeGenerator -import org.mifos.mobile.core.common.utils.Utils - -/** - * Created by dilpreet on 16/8/17. - */ -@AndroidEntryPoint -class QrCodeDisplayFragment : BaseFragment() { - - private var _binding: FragmentQrCodeDisplayBinding? = null - private val binding get() = _binding!! - - private var json: String? = null - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - (activity as? BaseActivity)?.showToolbar() - if (arguments != null) { - json = arguments?.getString(Constants.QR_DATA) - } - setHasOptionsMenu(true) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle?, - ): View { - _binding = FragmentQrCodeDisplayBinding.inflate(inflater, container, false) - binding.ivQrCode.setImageBitmap(QrCodeGenerator.encodeAsBitmap(json)) - return binding.root - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - super.onCreateOptionsMenu(menu, inflater) - inflater.inflate(R.menu.menu_qr_code_display, menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.item_qr_code_share -> { - val bitmapDrawable = binding.ivQrCode.drawable as BitmapDrawable - val uri = Utils.getImageUri(activity, bitmapDrawable.bitmap) - val intent = Intent() - intent.action = Intent.ACTION_SEND - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - intent.type = "image/*" - intent.putExtra(Intent.EXTRA_STREAM, uri) - startActivity(Intent.createChooser(intent, getString(R.string.choose_option))) - } - } - return super.onOptionsItemSelected(item) - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - - companion object { - fun newInstance(json: String?): QrCodeDisplayFragment { - val fragment = QrCodeDisplayFragment() - val args = Bundle() - args.putString(Constants.QR_DATA, json) - fragment.arguments = args - return fragment - } - } -} diff --git a/app/src/main/java/org/mifos/mobile/ui/qr_code_import/QrCodeImportFragment.kt b/app/src/main/java/org/mifos/mobile/ui/qr_code_import/QrCodeImportFragment.kt deleted file mode 100644 index 1ab704aa2a..0000000000 --- a/app/src/main/java/org/mifos/mobile/ui/qr_code_import/QrCodeImportFragment.kt +++ /dev/null @@ -1,217 +0,0 @@ -package org.mifos.mobile.ui.qr_code_import - -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.graphics.RectF -import android.net.Uri -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import com.google.gson.Gson -import com.google.gson.JsonSyntaxException -import com.google.zxing.Result -import com.isseiaoki.simplecropview.CropImageView -import kotlinx.coroutines.launch -import org.mifos.mobile.R -import org.mifos.mobile.databinding.FragmentQrCodeImportBinding -import org.mifos.mobile.ui.activities.base.BaseActivity -import org.mifos.mobile.ui.beneficiary_application.BeneficiaryApplicationComposeFragment -import org.mifos.mobile.core.model.enums.BeneficiaryState -import org.mifos.mobile.ui.fragments.base.BaseFragment -import org.mifos.mobile.core.common.Constants -import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary -import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedParcelable -import org.mifos.mobile.utils.Toaster -import java.io.FileNotFoundException -import java.io.InputStream - -/** - * Created by manishkumar on 19/05/18. - */ - -class QrCodeImportFragment : BaseFragment() { - - private var _binding: FragmentQrCodeImportBinding? = null - private val binding get() = _binding!! - - private val viewModel: QrCodeImportViewModel by viewModels() - - private lateinit var qrUri: Uri - private var uriValue: String? = null - private var mFrameRect: RectF? = null - private var inputStream: InputStream? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - if (arguments != null) { - uriValue = arguments?.getString(Constants.QR_IMAGE_URI) - qrUri = Uri.parse(uriValue) - } - setHasOptionsMenu(true) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle?, - ): View { - _binding = FragmentQrCodeImportBinding.inflate(inflater, container, false) - setToolbarTitle(getString(R.string.import_qr)) - // load the uri - setBitmapImage(qrUri) - with(binding) { - ivCropQrCode.setCompressFormat(Bitmap.CompressFormat.JPEG) - ivCropQrCode.setOutputMaxSize(150, 150) - ivCropQrCode.load(qrUri) - ?.initialFrameRect(mFrameRect) - ?.executeAsCompletable() - ivCropQrCode.setCropMode(CropImageView.CropMode.FREE) - ivCropQrCode.setInitialFrameScale(0.8f) - } - - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.qrCodeImportUiState.collect { - when (it) { - is QrCodeImportUiState.Loading -> showProgress() - - is QrCodeImportUiState.ShowError -> { - hideProgress() - showErrorReadingQr(getString(it.message)) - } - - is QrCodeImportUiState.HandleDecodedResult -> { - hideProgress() - handleDecodedResult(it.result) - } - - QrCodeImportUiState.Initial -> {} - } - } - } - } - - binding.btnProceed.setOnClickListener { - proceed() - } - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - // save data - outState.putParcelable(org.mifos.mobile.core.common.Constants.FRAME_RECT, binding.ivCropQrCode.actualCropRect) - outState.putParcelable(org.mifos.mobile.core.common.Constants.SOURCE_URI, binding.ivCropQrCode.sourceUri) - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - if (savedInstanceState != null) { - // restore data - mFrameRect = savedInstanceState.getCheckedParcelable(RectF::class.java, org.mifos.mobile.core.common.Constants.FRAME_RECT) - qrUri = savedInstanceState.getCheckedParcelable(Uri::class.java, org.mifos.mobile.core.common.Constants.SOURCE_URI)!! - } - } - - fun proceed() { -// viewModel.getDecodedResult(qrUri, binding.ivCropQrCode) - } - - /** - * It is called whenever any error occurs while executing a request - * - * @param message Error message that tells the user about the problem. - */ - private fun showErrorReadingQr(message: String?) { - Toaster.show(binding.root, message) - } - - /** - * CallBack for[CropImageView] which retrieves data from QRCode - * Opens [BeneficiaryApplicationFragment] with [BeneficiaryState] as - * `BeneficiaryState.CREATE_QR` - * - * @param result contains the results from decoded QR bitmap - */ - private fun handleDecodedResult(result: Result?) { - val gson = Gson() - try { - val beneficiary = gson.fromJson(result?.text, Beneficiary::class.java) - activity?.supportFragmentManager?.popBackStack() - (activity as BaseActivity?)?.replaceFragment( - BeneficiaryApplicationComposeFragment.newInstance(BeneficiaryState.CREATE_QR, beneficiary), - true, - R.id.container, - ) - } catch (e: JsonSyntaxException) { - Toast.makeText( - activity, - getString(R.string.invalid_qr), - Toast.LENGTH_SHORT, - ).show() - } - } - - /** - * Shows [org.mifos.mobile.utils.ProgressBarHandler] - */ - fun showProgress() { - showProgressBar() - } - - /** - * Hides [org.mifos.mobile.utils.ProgressBarHandler] - */ - fun hideProgress() { - hideProgressBar() - } - - override fun onDestroyView() { - super.onDestroyView() - hideProgress() - _binding = null - } - - /** - * Initializing UI - * - * @param qrImageUri contains Uri of qr code image - */ - private fun setBitmapImage(qrImageUri: Uri) { - try { - inputStream = context?.contentResolver?.openInputStream(qrImageUri) - } catch (e: FileNotFoundException) { - Toaster.show(binding.root, getString(R.string.error_fetching_image)) - } - val b = BitmapFactory.decodeStream(inputStream, null, null) - try { - if (inputStream != null) { - inputStream?.close() - } - } catch (e: Exception) { - Toaster.show(binding.root, getString(R.string.error_fetching_image)) - } - binding.ivCropQrCode.imageBitmap = b - } - - companion object { - fun newInstance(uri: Uri): QrCodeImportFragment { - val fragment = QrCodeImportFragment() - val args = Bundle() - args.putString(Constants.QR_IMAGE_URI, uri.toString()) - fragment.arguments = args - return fragment - } - } -} diff --git a/app/src/main/java/org/mifos/mobile/ui/recent_transactions/RecentTransactionsComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/recent_transactions/RecentTransactionsComposeFragment.kt index 1691f855be..59c02d5f72 100644 --- a/app/src/main/java/org/mifos/mobile/ui/recent_transactions/RecentTransactionsComposeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/recent_transactions/RecentTransactionsComposeFragment.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import dagger.hilt.android.AndroidEntryPoint import org.mifos.mobile.core.ui.component.mifosComposeView +import org.mifos.mobile.feature.recent_transaction.screens.RecentTransactionScreen import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.fragments.base.BaseFragment diff --git a/app/src/main/java/org/mifos/mobile/ui/registration/RegistrationActivity.kt b/app/src/main/java/org/mifos/mobile/ui/registration/RegistrationActivity.kt index 8892274cd6..b94d8e32e7 100644 --- a/app/src/main/java/org/mifos/mobile/ui/registration/RegistrationActivity.kt +++ b/app/src/main/java/org/mifos/mobile/ui/registration/RegistrationActivity.kt @@ -1,9 +1,14 @@ package org.mifos.mobile.ui.registration +import android.app.Activity import android.content.Intent import android.os.Bundle +import android.view.WindowInsets import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import androidx.compose.runtime.DisposableEffect +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat import androidx.navigation.compose.rememberNavController import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.feature.registration.navigation.RegistrationNavGraph @@ -14,6 +19,8 @@ import org.mifos.mobile.ui.login.LoginActivity class RegistrationActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + + WindowCompat.setDecorFitsSystemWindows(window, false) enableEdgeToEdge() setContent { MifosMobileTheme { diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsDetailFragment.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsDetailFragment.kt index d7c84a6e7f..23ddd9d6bf 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsDetailFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsDetailFragment.kt @@ -16,20 +16,16 @@ import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.ui.activities.SavingsAccountContainerActivity import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.client_charge.ClientChargeComposeFragment -import org.mifos.mobile.ui.savings_account_transaction.SavingAccountsTransactionComposeFragment -import org.mifos.mobile.ui.savings_account_application.SavingsAccountApplicationFragment -import org.mifos.mobile.ui.savings_account_withdraw.SavingsAccountWithdrawFragment -import org.mifos.mobile.ui.savings_make_transfer.SavingsMakeTransferFragment import org.mifos.mobile.ui.fragments.base.BaseFragment -import org.mifos.mobile.ui.qr_code_display.QrCodeDisplayComposeFragment -import org.mifos.mobile.ui.savings_make_transfer.SavingsMakeTransferComposeFragment +import org.mifos.mobile.ui.qr.QrCodeDisplayComposeFragment import org.mifos.mobile.core.common.Network import org.mifos.mobile.core.datastore.PreferencesHelper import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations import org.mifos.mobile.core.model.enums.AccountType import org.mifos.mobile.core.model.enums.ChargeType import org.mifos.mobile.core.model.enums.SavingsAccountState -import org.mifos.mobile.utils.QrCodeGenerator +import org.mifos.mobile.feature.savings.savings_account.SavingAccountsDetailViewModel +import org.mifos.mobile.feature.savings.savings_account.SavingsAccountDetailScreen import javax.inject.Inject /** @@ -167,7 +163,7 @@ class SavingAccountsDetailFragment : BaseFragment() { } private fun qrCodeClicked(savingsWithAssociations: SavingsWithAssociations) { - val accountDetailsInJson = QrCodeGenerator.getAccountDetailsInString( + val accountDetailsInJson = org.mifos.mobile.feature.qr.utils.QrCodeGenerator.getAccountDetailsInString( savingsWithAssociations.accountNo, preferencesHelper.officeName, AccountType.SAVINGS, diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFragment.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsTransactionFragment.kt similarity index 99% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFragment.kt rename to app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsTransactionFragment.kt index d1191ddd3b..1983febde9 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsTransactionFragment.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_transaction +package org.mifos.mobile.ui.savings_account import android.content.Intent import android.net.Uri @@ -42,10 +42,10 @@ import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getChec import org.mifos.mobile.utils.StatusUtils import org.mifos.mobile.utils.Toaster import org.mifos.mobile.core.common.utils.getDatePickerDialog +import org.mifos.mobile.feature.savings.savings_account_transaction.SavingAccountsTransactionViewModel import java.time.Instant import javax.inject.Inject - /** * Created by dilpreet on 6/3/17. */ diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationFragment.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountApplicationFragment.kt similarity index 92% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationFragment.kt rename to app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountApplicationFragment.kt index f70414837a..e2e17c2ec7 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountApplicationFragment.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_application +package org.mifos.mobile.ui.savings_account import android.os.Bundle import android.view.LayoutInflater @@ -16,6 +16,8 @@ import org.mifos.mobile.core.common.Constants import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedParcelable import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedSerializable +import org.mifos.mobile.feature.savings.savings_account_application.SavingsAccountApplicationScreen +import org.mifos.mobile.feature.savings.savings_account_application.SavingsAccountApplicationViewModel /* * Created by saksham on 30/June/2018 diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingsAccountTransactionComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountTransactionComposeFragment.kt similarity index 87% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingsAccountTransactionComposeFragment.kt rename to app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountTransactionComposeFragment.kt index 9c57916bec..79e9217f09 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingsAccountTransactionComposeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountTransactionComposeFragment.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_transaction +package org.mifos.mobile.ui.savings_account import android.os.Bundle import android.view.LayoutInflater @@ -10,6 +10,8 @@ import org.mifos.mobile.core.ui.component.mifosComposeView import org.mifos.mobile.ui.activities.SavingsAccountContainerActivity import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.core.common.Constants +import org.mifos.mobile.feature.savings.savings_account_transaction.SavingAccountsTransactionViewModel +import org.mifos.mobile.feature.savings.savings_account_transaction.SavingsAccountTransactionScreen @AndroidEntryPoint diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_withdraw/SavingsAccountWithdrawFragment.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountWithdrawFragment.kt similarity index 91% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_withdraw/SavingsAccountWithdrawFragment.kt rename to app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountWithdrawFragment.kt index d52d91b5d4..7f6543f31a 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_withdraw/SavingsAccountWithdrawFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountWithdrawFragment.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_withdraw +package org.mifos.mobile.ui.savings_account import android.os.Bundle import android.view.LayoutInflater @@ -13,6 +13,8 @@ import org.mifos.mobile.ui.activities.SavingsAccountContainerActivity import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedParcelable +import org.mifos.mobile.feature.savings.savings_account_withdraw.SavingsAccountWithdrawScreen +import org.mifos.mobile.feature.savings.savings_account_withdraw.SavingsAccountWithdrawViewModel /* * Created by saksham on 02/July/2018 diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsMakeTransferComposeFragment.kt similarity index 94% rename from app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferComposeFragment.kt rename to app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsMakeTransferComposeFragment.kt index 9e74def24a..0aee7de533 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferComposeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsMakeTransferComposeFragment.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_make_transfer +package org.mifos.mobile.ui.savings_account import android.os.Bundle import android.view.LayoutInflater @@ -15,7 +15,9 @@ import org.mifos.mobile.ui.transfer_process.TransferProcessComposeFragment import org.mifos.mobile.core.model.entity.payload.TransferPayload import org.mifos.mobile.core.common.utils.DateHelper import org.mifos.mobile.core.common.utils.getTodayFormatted - +import org.mifos.mobile.feature.savings.savings_make_transfer.ReviewTransferPayload +import org.mifos.mobile.feature.savings.savings_make_transfer.SavingsMakeTransferScreen +import org.mifos.mobile.feature.savings.savings_make_transfer.SavingsMakeTransferViewModel @AndroidEntryPoint class SavingsMakeTransferComposeFragment : BaseFragment() { diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferFragment.kt b/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferFragment.kt deleted file mode 100644 index 51be6d7aca..0000000000 --- a/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferFragment.kt +++ /dev/null @@ -1,432 +0,0 @@ -package org.mifos.mobile.ui.savings_make_transfer - -import dagger.hilt.android.AndroidEntryPoint -import org.mifos.mobile.ui.fragments.base.BaseFragment - -/** - * Created by Rajan Maurya on 10/03/17. - */ -@AndroidEntryPoint -class SavingsMakeTransferFragment : BaseFragment() { -// -// private var _binding: FragmentSavingsMakeTransferBinding? = null -// private val binding get() = _binding!! -// private val viewModel: SavingsMakeTransferViewModel by viewModels() -// private var transferPayload: TransferPayload? = null -// private var transferDate: String? = null -// private var toAccountOption: AccountOption? = null -// private var fromAccountOption: AccountOption? = null -// private var accountOptionsTemplate: AccountOptionsTemplate? = null -// private var transferType: String? = null -// private var payTo: String? = null -// private var payFrom: String? = null -// private var accountId: Long? = 0 -// private var outStandingBalance: Double? = 0.0 -// private var isLoanRepayment = false -// private var sweetUIErrorHandler: SweetUIErrorHandler? = null -// -// override fun onCreate(savedInstanceState: Bundle?) { -// super.onCreate(savedInstanceState) -// (activity as? BaseActivity)?.showToolbar() -// if (arguments != null) { -// accountId = arguments?.getLong(Constants.ACCOUNT_ID) -// transferType = arguments?.getString(Constants.TRANSFER_TYPE) -// if (arguments?.getBoolean(Constants.LOAN_REPAYMENT, false) == true) { -// isLoanRepayment = true -// outStandingBalance = arguments?.getDouble(Constants.OUTSTANDING_BALANCE) -// } -// } -// setHasOptionsMenu(true) -// } -// -// override fun onCreateView( -// inflater: LayoutInflater, -// container: ViewGroup?, -// savedInstanceState: Bundle?, -// ): View { -// _binding = FragmentSavingsMakeTransferBinding.inflate(inflater, container, false) -// setToolbarTitle(getString(R.string.transfer)) -// sweetUIErrorHandler = SweetUIErrorHandler(activity, binding.root) -// showUserInterface() -// if (savedInstanceState == null) { -// //viewModel.loanAccountTransferTemplate() -// } -// return binding.root -// } -// -// override fun onViewCreated(view: View, savedInstanceState: Bundle?) { -// super.onViewCreated(view, savedInstanceState) -// -// with(binding) { -// btnCancelTransfer.setOnClickListener { -// cancelTransfer() -// } -// -// btnReviewTransfer.setOnClickListener { -// reviewTransfer() -// } -// -// btnPayFrom.setOnClickListener { -// payFromSelected() -// } -// -// btnPayTo.setOnClickListener { -// payToSelected() -// } -// -// btnAmount.setOnClickListener { -// amountSet() -// } -// -// layoutError.btnTryAgain.setOnClickListener { -// onRetry() -// } -// } -// -//// viewLifecycleOwner.lifecycleScope.launch { -//// repeatOnLifecycle(Lifecycle.State.STARTED) { -//// viewModel.savingsMakeTransferUiState.collect { state -> -//// when (state) { -//// SavingsAccountUiState.Loading -> showProgress() -//// -//// is SavingsAccountUiState.ErrorMessage -> { -//// hideProgress() -//// showError(context?.getString(R.string.error_fetching_account_transfer_template)) -//// } -//// -//// is SavingsAccountUiState.ShowSavingsAccountTemplate -> { -//// hideProgress() -//// showSavingsAccountTemplate(state.accountOptionsTemplate) -//// } -//// -//// is SavingsAccountUiState.Initial -> {} -//// -//// else -> throw IllegalStateException("Unexpected state : $state") -//// } -//// } -//// } -//// } -// } -// -// override fun onSaveInstanceState(outState: Bundle) { -// super.onSaveInstanceState(outState) -// outState.putParcelable(Constants.TEMPLATE, accountOptionsTemplate) -// } -// -// override fun onActivityCreated(savedInstanceState: Bundle?) { -// super.onActivityCreated(savedInstanceState) -// if (savedInstanceState != null) { -// showSavingsAccountTemplate( -// savedInstanceState.getCheckedParcelable( -// AccountOptionsTemplate::class.java, -// Constants.TEMPLATE -// ) -// ) } -// } -// -// /** -// * Checks validation of `etRemark` and then opens [TransferProcessComposeFragment] for -// * initiating the transfer -// */ -// private fun reviewTransfer() { -// if (binding.etRemark.text.toString().trim { it <= ' ' } == "") { -// showToaster(getString(R.string.remark_is_mandatory)) -// return -// } -// transferPayload = TransferPayload() -// transferPayload?.fromAccountId = fromAccountOption?.accountId -// transferPayload?.fromClientId = fromAccountOption?.clientId -// transferPayload?.fromAccountType = fromAccountOption?.accountType?.id -// transferPayload?.fromOfficeId = fromAccountOption?.officeId -// transferPayload?.toOfficeId = toAccountOption?.officeId -// transferPayload?.toAccountId = toAccountOption?.accountId -// transferPayload?.toClientId = toAccountOption?.clientId -// transferPayload?.toAccountType = toAccountOption?.accountType?.id -// transferPayload?.transferDate = transferDate -// transferPayload?.transferAmount = binding.amountField.text.toString().toDouble() -// transferPayload?.transferDescription = binding.etRemark.text.toString() -// transferPayload?.fromAccountNumber = fromAccountOption?.accountNo -// transferPayload?.toAccountNumber = toAccountOption?.accountNo -// (activity as BaseActivity?)?.replaceFragment( -// TransferProcessComposeFragment.newInstance( -// transferPayload, -// TransferType.SELF, -// ), -// true, -// R.id.container, -// ) -// } -// -// /** -// * Cancels the transfer by popping current Fragment -// */ -// private fun cancelTransfer() { -// activity?.supportFragmentManager?.popBackStack() -// } -// -// private fun onRetry() { -// if (Network.isConnected(context)) { -// sweetUIErrorHandler?.hideSweetErrorLayoutUI( -// binding.llMakeTransfer, -// binding.layoutError.root, -// ) -// //viewModel.loanAccountTransferTemplate() -// } else { -// Toaster.show(binding.root, getString(R.string.internet_not_connected)) -// } -// } -// -// /** -// * Setting up basic components -// */ -// fun showUserInterface() { -// binding.processOne.setCurrentActive() -// binding.payFromField.setOnItemClickListener { _, _, position, _ -> -// toAccountOption = accountOptionsTemplate?.toAccountOptions?.get(position) -// payTo = toAccountOption?.accountNo -// println("paytofield item selected payTo:$payTo") -// updateDetails() -// } -// binding.payToField.setOnItemClickListener { _, _, position, _ -> -// fromAccountOption = accountOptionsTemplate?.fromAccountOptions?.get(position) -// payFrom = fromAccountOption?.accountNo -// println("payfrom item selected payFrom:$payFrom") -// updateDetails() -// } -// transferDate = DateHelper.getSpecificFormat( -// DateHelper.FORMAT_dd_MMMM_yyyy, -// getTodayFormatted(), -// ) -// if (isLoanRepayment) { -// binding.amountField.setText(outStandingBalance.toString()) -// binding.amountFieldWrapper.isFocusable = false -// } -// } -// -// private fun updateDetails() { -//// when (transferType) { -//// Constants.TRANSFER_PAY_TO -> { -//// setToolbarTitle(getString(R.string.deposit)) -//// toAccountOption = -//// viewModel.searchAccount(accountOptionsTemplate?.toAccountOptions, accountId) -//// binding.payToFieldWrapper.isEnabled = false -//// binding.processOne.setCurrentCompleted() -//// } -//// -//// Constants.TRANSFER_PAY_FROM -> { -//// setToolbarTitle(getString(R.string.transfer)) -//// fromAccountOption = -//// viewModel.searchAccount(accountOptionsTemplate?.fromAccountOptions, accountId) -//// binding.payFromFieldWrapper.isEnabled = false -//// binding.payFromFieldWrapper.visibility = View.VISIBLE -//// binding.processTwo.setCurrentCompleted() -//// } -//// } -// } -// -// /** -// * Provides with `accountOptionsTemplate` fetched from server which is used to update -// * `listPayFrom` and `listPayTo` -// * -// * @param accountOptionsTemplate Template for account transfer -// */ -// private fun showSavingsAccountTemplate(accountOptionsTemplate: AccountOptionsTemplate?) { -// this.accountOptionsTemplate = accountOptionsTemplate -//// binding.payToField.setAdapter( -//// AccountsSpinnerAdapter( -//// requireContext(), -//// viewModel.getAccountNumbers( -//// accountOptionsTemplate?.toAccountOptions, -//// false, -//// context?.getString(R.string.account_type_loan) -//// ), -//// ), -//// ) -//// binding.payFromField.setAdapter( -//// AccountsSpinnerAdapter( -//// requireContext(), -//// viewModel.getAccountNumbers( -//// accountOptionsTemplate?.toAccountOptions, -//// true, -//// context?.getString(R.string.account_type_loan) -//// ), -//// ), -//// ) -// } -// -// /** -// * Shows a {@link Snackbar} with `message` -// *f -// * @param message String to be shown -// */ -// private fun showToaster(message: String?) { -// Toaster.show(binding.root, message) -// } -// -// /** -// * It is called whenever any error occurs while executing a request -// * -// * @param message Error message that tells the user about the problem. -// */ -// fun showError(message: String?) { -// if (!Network.isConnected(context)) { -// sweetUIErrorHandler?.showSweetNoInternetUI( -// binding.llMakeTransfer, -// binding.layoutError.root, -// ) -// } else { -// sweetUIErrorHandler?.showSweetErrorUI( -// message, -// binding.llMakeTransfer, -// binding.layoutError.root, -// ) -// Toaster.show(binding.root, message) -// } -// } -// -// fun showProgress() { -// binding.llMakeTransfer.visibility = View.GONE -// showProgressBar() -// } -// -// fun hideProgress() { -// binding.llMakeTransfer.visibility = View.VISIBLE -// hideProgressBar() -// } -// -// /** -// * Callback for `spPayFrom` and `spPayTo` -// */ -// -// /** -// * Disables `spPayTo` [Spinner] and sets `pvOne` to completed and make -// * `pvTwo` active -// */ -// private fun payToSelected() { -// with(binding) { -// processOne.setCurrentCompleted() -// processTwo.setCurrentActive() -// btnPayTo.visibility = View.GONE -// btnPayFrom.visibility = View.VISIBLE -// payFromFieldWrapper.visibility = View.VISIBLE -// payToFieldWrapper.isEnabled = false -// } -// } -// -// /** -// * Checks validation of `spPayTo` [Spinner].

-// * Disables `spPayFrom` [Spinner] and sets `pvTwo` to completed and make -// * `pvThree` active -// */ -// private fun payFromSelected() { -// if (payTo == payFrom) { -// showToaster(getString(R.string.error_same_account_transfer)) -// return -// } -// with(binding) { -// processTwo.setCurrentCompleted() -// processThree.setCurrentActive() -// btnPayFrom.visibility = View.GONE -// amountFieldWrapper.visibility = View.VISIBLE -// btnAmount.visibility = View.VISIBLE -// payFromFieldWrapper.isEnabled = false -// } -// } -// -// /** -// * Checks validation of `etAmount` [EditText].

-// * Disables `etAmount` and sets `pvThree` to completed and make -// * `pvFour` active -// */ -// private fun amountSet() { -// if (binding.amountField.text.toString() == "") { -// showToaster(getString(R.string.enter_amount)) -// return -// } -// if (binding.amountField.text.toString() == ".") { -// showToaster(getString(R.string.invalid_amount)) -// return -// } -// if (binding.amountField.text.toString().toDouble() == 0.0) { -// showToaster(getString(R.string.amount_greater_than_zero)) -// return -// } -// with(binding) { -// processThree.setCurrentCompleted() -// processFour.setCurrentActive() -// btnAmount.visibility = View.GONE -// tvEnterRemark.visibility = View.GONE -// etRemark.visibility = View.VISIBLE -// llReview.visibility = View.VISIBLE -// amountFieldWrapper.isEnabled = false -// } -// } -// -// override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { -// super.onCreateOptionsMenu(menu, inflater) -// inflater.inflate(R.menu.menu_transfer, menu) -// Utils.setToolbarIconColor(activity, menu, R.color.white) -// } -// -// override fun onOptionsItemSelected(item: MenuItem): Boolean { -// if (item.itemId == R.id.menu_refresh_transfer) { -// val transaction = fragmentManager?.beginTransaction() -// val currFragment = activity?.supportFragmentManager -// ?.findFragmentById(R.id.container) -// if (currFragment != null) { -// transaction?.detach(currFragment) -// transaction?.attach(currFragment) -// } -// transaction?.commit() -// } -// return super.onOptionsItemSelected(item) -// } -// -// override fun onDestroyView() { -// super.onDestroyView() -// hideProgress() -// hideMifosProgressDialog() -// _binding = null -// } -// -// companion object { -// /** -// * Provides an instance of [SavingsMakeTransferFragment], use `transferType` as -// * `Constants.TRANSFER_PAY_TO` when we want to deposit and -// * `Constants.TRANSFER_PAY_FROM` when we want to make a transfer -// * -// * @param accountId Saving account Id -// * @param transferType Type of transfer i.e. `Constants.TRANSFER_PAY_TO` or -// * `Constants.TRANSFER_PAY_FROM` -// * @return Instance of [SavingsMakeTransferFragment] -// */ -// fun newInstance(accountId: Long?, transferType: String?): SavingsMakeTransferFragment { -// val transferFragment = SavingsMakeTransferFragment() -// val args = Bundle() -// if (accountId != null) args.putLong(Constants.ACCOUNT_ID, accountId) -// args.putString(Constants.TRANSFER_TYPE, transferType) -// transferFragment.arguments = args -// return transferFragment -// } -// -// fun newInstance( -// accountId: Long?, -// outstandingBalance: Double?, -// transferType: String?, -// ): SavingsMakeTransferFragment { -// val transferFragment = SavingsMakeTransferFragment() -// val args = Bundle() -// if (accountId != null) args.putLong(Constants.ACCOUNT_ID, accountId) -// args.putString(Constants.TRANSFER_TYPE, transferType) -// if (outstandingBalance != null) { -// args.putDouble( -// Constants.OUTSTANDING_BALANCE, -// outstandingBalance, -// ) -// } -// args.putBoolean(Constants.LOAN_REPAYMENT, true) -// transferFragment.arguments = args -// return transferFragment -// } -// } -} diff --git a/app/src/main/java/org/mifos/mobile/ui/settings/SettingsComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/settings/SettingsComposeFragment.kt index 95651306f1..cf5a041b4a 100644 --- a/app/src/main/java/org/mifos/mobile/ui/settings/SettingsComposeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/settings/SettingsComposeFragment.kt @@ -9,6 +9,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.mifosComposeView import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.feature.settings.SettingsScreen import org.mifos.mobile.ui.activities.HomeActivity import org.mifos.mobile.ui.activities.PassCodeActivity import org.mifos.mobile.ui.activities.base.BaseActivity diff --git a/app/src/main/java/org/mifos/mobile/ui/settings/SettingsFragment.kt b/app/src/main/java/org/mifos/mobile/ui/settings/SettingsFragment.kt deleted file mode 100644 index 1d86d4ffcf..0000000000 --- a/app/src/main/java/org/mifos/mobile/ui/settings/SettingsFragment.kt +++ /dev/null @@ -1,196 +0,0 @@ -package org.mifos.mobile.ui.settings - -import android.content.Intent -import android.content.SharedPreferences -import android.content.SharedPreferences.OnSharedPreferenceChangeListener -import android.os.Build -import android.os.Bundle -import androidx.appcompat.app.AppCompatDelegate -import androidx.fragment.app.DialogFragment -import androidx.preference.ListPreference -import androidx.preference.Preference -import androidx.preference.PreferenceFragmentCompat -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.mifos.mobile.passcode.utils.PasscodePreferencesHelper -import dagger.hilt.android.AndroidEntryPoint -import org.mifos.mobile.R -import org.mifos.mobile.ui.activities.PassCodeActivity -import org.mifos.mobile.ui.activities.base.BaseActivity -import org.mifos.mobile.ui.update_password.UpdatePasswordFragment -import org.mifos.mobile.utils.ConfigurationDialogFragmentCompat -import org.mifos.mobile.utils.ConfigurationPreference -import org.mifos.mobile.core.datastore.PreferencesHelper -import org.mifos.mobile.core.common.utils.LanguageHelper -import java.util.Locale - -/** - * Created by dilpreet on 02/10/17. - */ -@AndroidEntryPoint -class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListener { - - private val prefsHelper by lazy { PreferencesHelper(requireContext().applicationContext) } - var preference: android.preference.Preference? = null - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - addPreferencesFromResource(R.xml.settings_preference) - findPreference(getString(R.string.theme_type)).setOnPreferenceClickListener { - val previouslySelectedTheme = prefsHelper.appTheme - - MaterialAlertDialogBuilder(requireContext()) - .setTitle(getString(R.string.change_app_theme)) - .setSingleChoiceItems( - resources.getStringArray(R.array.themes), - previouslySelectedTheme, - ) { dialog, selectedTheme -> - prefsHelper.applyTheme(AppTheme.fromIndex(selectedTheme)) - dialog.dismiss() - } - .show() - return@setOnPreferenceClickListener true - } - findPreference(getString(R.string.passcode)).setOnPreferenceClickListener { - val passCodePreferencesHelper = PasscodePreferencesHelper(activity) - val currPassCode = passCodePreferencesHelper.passCode - passCodePreferencesHelper.savePassCode("") - val intent = Intent(activity, PassCodeActivity::class.java).apply { - putExtra(org.mifos.mobile.core.common.Constants.CURR_PASSWORD, currPassCode) - putExtra(org.mifos.mobile.core.common.Constants.IS_TO_UPDATE_PASS_CODE, true) - } - preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) - startActivity(intent) - true - } - when (preference?.key) { - getString(R.string.password) -> { - // TODO("create changePasswordActivity and implement the logic for password change") - } - - getString(R.string.passcode) -> { - activity?.let { - val passCodePreferencesHelper = PasscodePreferencesHelper(activity) - val currPassCode = passCodePreferencesHelper.passCode - passCodePreferencesHelper.savePassCode("") - val intent = Intent(it, PassCodeActivity::class.java).apply { - putExtra(org.mifos.mobile.core.common.Constants.CURR_PASSWORD, currPassCode) - putExtra(org.mifos.mobile.core.common.Constants.IS_TO_UPDATE_PASS_CODE, true) - } - preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) - startActivity(intent) - } - } - } - } - - override fun onResume() { - super.onResume() - (activity as? BaseActivity)?.showToolbar() - activity?.title = getString(R.string.settings) - preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this) - } - - override fun onPause() { - super.onPause() - preferenceScreen.sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) - } - - override fun onDisplayPreferenceDialog(preference: Preference) { - var dialogFragment: DialogFragment? = null - if (preference is ConfigurationPreference) { - dialogFragment = ConfigurationDialogFragmentCompat() - val bundle = Bundle(1) - bundle.putString("key", preference.getKey()) - dialogFragment.setArguments(bundle) - } - if (dialogFragment != null) { - dialogFragment.setTargetFragment(this, 0) - dialogFragment.show( - parentFragmentManager, - "android.support.v7.preference.PreferenceFragment.DIALOG", - ) - } else { - super.onDisplayPreferenceDialog(preference) - } - } - - override fun onPreferenceTreeClick(preference: Preference): Boolean { - when (preference.key) { - org.mifos.mobile.core.common.Constants.PASSWORD -> (activity as BaseActivity?)?.replaceFragment( - UpdatePasswordFragment.newInstance(), - true, - R.id.container, - ) - } - return super.onPreferenceTreeClick(preference) - } - - companion object { - @JvmStatic - fun newInstance(): SettingsFragment { - return SettingsFragment() - } - } - - override fun onSharedPreferenceChanged(p0: SharedPreferences?, p1: String?) { - val preference = findPreference(p1) - if (preference is ListPreference) { - val isSystemLanguage = - (preference.value == resources.getStringArray(R.array.languages_value)[0]) - prefsHelper.putBoolean( - context?.getString(R.string.default_system_language), - isSystemLanguage - ) - if (!isSystemLanguage) { - LanguageHelper.setLocale(context, preference.value) - } else { - if (!resources.getStringArray(R.array.languages_value) - .contains(Locale.getDefault().language) - ) { - LanguageHelper.setLocale(context, "en") - } else { - LanguageHelper.setLocale(context, Locale.getDefault().language) - } - } - val intent = Intent(activity, activity?.javaClass) - intent.putExtra(org.mifos.mobile.core.common.Constants.HAS_SETTINGS_CHANGED, true) - startActivity(intent) - activity?.finish() - } - } -} - -enum class AppTheme { - SYSTEM, LIGHT, DARK; - - companion object { - fun fromIndex(index: Int): AppTheme = when (index) { - 1 -> LIGHT - 2 -> DARK - else -> SYSTEM - } - } -} - -fun PreferencesHelper.applySavedTheme() { - val applicationTheme = AppTheme.fromIndex(this.appTheme) - AppCompatDelegate.setDefaultNightMode( - when { - applicationTheme == AppTheme.DARK -> AppCompatDelegate.MODE_NIGHT_YES - applicationTheme == AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO - Build.VERSION.SDK_INT > Build.VERSION_CODES.P -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - else -> AppCompatDelegate.MODE_NIGHT_NO - }, - ) -} - -fun PreferencesHelper.applyTheme(applicationTheme: AppTheme) { - this.appTheme = applicationTheme.ordinal - AppCompatDelegate.setDefaultNightMode( - when { - applicationTheme == AppTheme.DARK -> AppCompatDelegate.MODE_NIGHT_YES - applicationTheme == AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO - Build.VERSION.SDK_INT > Build.VERSION_CODES.P -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM - else -> AppCompatDelegate.MODE_NIGHT_NO - }, - ) -} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferComposeFragment.kt index 94c80bb696..35bb45fc43 100644 --- a/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferComposeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferComposeFragment.kt @@ -14,6 +14,8 @@ import org.mifos.mobile.ui.fragments.base.BaseFragment import org.mifos.mobile.ui.transfer_process.TransferProcessComposeFragment import org.mifos.mobile.core.common.utils.DateHelper import org.mifos.mobile.core.common.utils.getTodayFormatted +import org.mifos.mobile.feature.third.party.transfer.third_party_transfer.ThirdPartyTransferPayload +import org.mifos.mobile.feature.third.party.transfer.third_party_transfer.ThirdPartyTransferScreen @AndroidEntryPoint class ThirdPartyTransferComposeFragment : BaseFragment() { diff --git a/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferFragment.kt b/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferFragment.kt deleted file mode 100644 index 8a1aacb8a6..0000000000 --- a/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferFragment.kt +++ /dev/null @@ -1,478 +0,0 @@ -package org.mifos.mobile.ui.third_party_transfer - -/* - -import android.os.Bundle -import android.os.Parcelable -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import android.widget.AdapterView -import android.widget.AdapterView.OnItemSelectedListener -import android.widget.EditText -import android.widget.Spinner -import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import com.github.therajanmaurya.sweeterror.SweetUIErrorHandler -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch -import org.mifos.mobile.R -import org.mifos.mobile.databinding.FragmentThirdPartyTransferBinding -import org.mifos.mobile.models.beneficiary.Beneficiary -import org.mifos.mobile.models.beneficiary.BeneficiaryDetail -import org.mifos.mobile.models.payload.AccountDetail -import org.mifos.mobile.models.payload.TransferPayload -import org.mifos.mobile.models.templates.account.AccountOption -import org.mifos.mobile.models.templates.account.AccountOptionsTemplate -import org.mifos.mobile.ui.activities.base.BaseActivity -import org.mifos.mobile.ui.adapters.AccountsSpinnerAdapter -import org.mifos.mobile.ui.adapters.BeneficiarySpinnerAdapter -import org.mifos.mobile.ui.beneficiary.presentation.BeneficiaryAddOptionsFragment -import org.mifos.mobile.ui.enums.TransferType -import org.mifos.mobile.ui.fragments.TransferProcessFragment -import org.mifos.mobile.ui.fragments.base.BaseFragment -import org.mifos.mobile.ui.transfer_process.TransferProcessComposeFragment -import org.mifos.mobile.core.common.Constants -import org.mifos.mobile.core.common.utils.DateHelper -import org.mifos.mobile.core.common.Network -import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedArrayListFromParcelable -import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedParcelable -import org.mifos.mobile.utils.ThirdPartyTransferUiState -import org.mifos.mobile.utils.Toaster -import org.mifos.mobile.core.common.utils.Utils -import org.mifos.mobile.utils.getTodayFormatted - -/** - * Created by dilpreet on 21/6/17. - */ -@AndroidEntryPoint -class ThirdPartyTransferFragment : BaseFragment(), OnItemSelectedListener { - - private var _binding: FragmentThirdPartyTransferBinding? = null - private val binding get() = _binding!! - - private val viewModel: ThirdPartyTransferViewModel by viewModels() - - private val listBeneficiary: MutableList = ArrayList() - private val listPayFrom: MutableList = ArrayList() - private var beneficiaries: List? = null - private var beneficiaryAdapter: BeneficiarySpinnerAdapter? = null - private var payFromAdapter: AccountsSpinnerAdapter? = null - private var fromAccountOption: AccountOption? = null - private var beneficiaryAccountOption: AccountOption? = null - private var accountOptionsTemplate: AccountOptionsTemplate? = null - private var transferDate: String? = null - private var sweetUIErrorHandler: SweetUIErrorHandler? = null - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle?, - ): View { - _binding = FragmentThirdPartyTransferBinding.inflate(inflater, container, false) - setToolbarTitle(getString(R.string.third_party_transfer)) - sweetUIErrorHandler = SweetUIErrorHandler(activity, binding.root) - showUserInterface() - if (savedInstanceState == null) { - viewModel.loadTransferTemplate() - } - - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.thirdPartyTransferUiState.collect { - when (it) { - is ThirdPartyTransferUiState.Loading -> showProgress() - is ThirdPartyTransferUiState.Error -> { - hideProgress() - getString(it.message) - } - - is ThirdPartyTransferUiState.ShowThirdPartyTransferTemplate -> { - hideProgress() - showThirdPartyTransferTemplate(it.accountOptionsTemplate) - } - - is ThirdPartyTransferUiState.ShowBeneficiaryList -> { - hideProgress() - showBeneficiaryList(it.beneficiaries) - } - - ThirdPartyTransferUiState.Initial -> {} - } - } - } - } - - with(binding) { - btnReviewTransfer.setOnClickListener { - reviewTransfer() - } - - btnPayFrom.setOnClickListener { - payFromSelected() - } - - btnPayTo.setOnClickListener { - payToSelected() - } - - btnAmount.setOnClickListener { - amountSet() - } - - btnCancelTransfer.setOnClickListener { - cancelTransfer() - } - - btnAddBeneficiary.setOnClickListener { - addBeneficiary() - } - layoutError.btnTryAgain.setOnClickListener { - onRetry() - } - } - - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - accountOptionsTemplate?.let { - outState.putParcelable(Constants.TEMPLATE, it) - } - beneficiaries?.let { - outState.putParcelableArrayList( - Constants.BENEFICIARY, - ArrayList(it), - ) - } - } - - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - if (savedInstanceState != null) { - showThirdPartyTransferTemplate( - savedInstanceState.getCheckedParcelable( - AccountOptionsTemplate::class.java, - Constants.TEMPLATE - ) - ) - val tempBeneficiaries: List = - savedInstanceState.getCheckedArrayListFromParcelable( - Beneficiary::class.java, - Constants.BENEFICIARY, - ) ?: listOf() - showBeneficiaryList(tempBeneficiaries) - } - } - - /** - * Setting up basic components - */ - fun showUserInterface() { - payFromAdapter = activity?.applicationContext?.let { - AccountsSpinnerAdapter(it, listPayFrom) - } - payFromAdapter?.setDropDownViewResource(android.R.layout.select_dialog_singlechoice) - binding.spPayFrom.adapter = payFromAdapter - binding.spPayFrom.onItemSelectedListener = this - beneficiaryAdapter = activity?.applicationContext?.let { - BeneficiarySpinnerAdapter( - it, - R.layout.beneficiary_spinner_layout, - listBeneficiary, - ) - } - beneficiaryAdapter?.setDropDownViewResource(android.R.layout.select_dialog_singlechoice) - binding.spBeneficiary.adapter = beneficiaryAdapter - binding.spBeneficiary.onItemSelectedListener = this - transferDate = DateHelper.getSpecificFormat( - DateHelper.FORMAT_dd_MMMM_yyyy, - getTodayFormatted(), - ) - binding.processOne.setCurrentActive() - } - - /** - * Checks validation of `etRemark` and then opens [TransferProcessComposeFragment] for - * initiating the transfer - */ - private fun reviewTransfer() { - if (binding.etAmount.text.toString() == "") { - Toaster.show(binding.root, getString(R.string.enter_amount)) - return - } - if (binding.etAmount.text.toString() == ".") { - Toaster.show(binding.root, getString(R.string.invalid_amount)) - return - } - if (binding.etRemark.text.toString().trim { it <= ' ' } == "") { - Toaster.show(binding.root, getString(R.string.remark_is_mandatory)) - return - } - if (binding.spBeneficiary.selectedItem.toString() == binding.spPayFrom.selectedItem.toString()) { - Toaster.show(binding.root, getString(R.string.error_same_account_transfer)) - return - } - val transferPayload = TransferPayload() - transferPayload.fromAccountId = fromAccountOption?.accountId - transferPayload.fromClientId = fromAccountOption?.clientId - transferPayload.fromAccountNumber = fromAccountOption?.accountNo - transferPayload.fromAccountType = fromAccountOption?.accountType?.id - transferPayload.fromOfficeId = fromAccountOption?.officeId - transferPayload.toOfficeId = beneficiaryAccountOption?.officeId - transferPayload.toAccountId = beneficiaryAccountOption?.accountId - transferPayload.toClientId = beneficiaryAccountOption?.clientId - transferPayload.toAccountNumber = beneficiaryAccountOption?.accountNo - transferPayload.toAccountType = beneficiaryAccountOption?.accountType?.id - transferPayload.transferDate = transferDate - transferPayload.transferAmount = binding.etAmount.text.toString().toDouble() - transferPayload.transferDescription = binding.etRemark.text.toString() - transferPayload.fromAccountNumber = fromAccountOption?.accountNo - transferPayload.toAccountNumber = beneficiaryAccountOption?.accountNo - (activity as BaseActivity?)?.replaceFragment( - TransferProcessComposeFragment.newInstance( - transferPayload, - TransferType.TPT, - ), - true, - R.id.container, - ) - } - - /** - * Shows a {@link Snackbar} with `message` - * - * @param msg String to be shown - */ - private fun showToaster(msg: String?) { - Toaster.show(binding.root, msg) - } - - /** - * Provides with `accountOptionsTemplate` fetched from server which is used to update - * `listPayFrom` - * - * @param accountOptionsTemplate Template for account transfer - */ - private fun showThirdPartyTransferTemplate(accountOptionsTemplate: AccountOptionsTemplate?) { - this.accountOptionsTemplate = accountOptionsTemplate - listPayFrom.clear() - viewModel.getAccountNumbersFromAccountOptions( - accountOptionsTemplate?.fromAccountOptions, - context?.getString(R.string.account_type_loan) - ) - .let { listPayFrom.addAll(it) } - payFromAdapter?.notifyDataSetChanged() - } - - /** - * Provides with `beneficiaries` fetched from server which is used to update - * `listBeneficiary` - * - * @param beneficiaries List of [Beneficiary] linked with user's account - */ - private fun showBeneficiaryList(beneficiaries: List?) { - this.beneficiaries = beneficiaries - listBeneficiary.clear() - viewModel.getAccountNumbersFromBeneficiaries(beneficiaries) - .let { listBeneficiary.addAll(it) } - beneficiaryAdapter?.notifyDataSetChanged() - } - - /** - * Disables `spPayFrom` [Spinner] and sets `pvOne` to completed and make - * `pvThree` pvTwo - */ - private fun payFromSelected() { - with(binding) { - processOne.setCurrentCompleted() - processTwo.setCurrentActive() - btnPayFrom.visibility = View.GONE - tvSelectBeneficary.visibility = View.GONE - if (listBeneficiary.isNotEmpty()) { - btnPayTo.visibility = View.VISIBLE - spBeneficiary.visibility = View.VISIBLE - spPayFrom.isEnabled = false - } else { - tvAddBeneficiaryMsg.visibility = View.VISIBLE - btnAddBeneficiary.visibility = View.VISIBLE - } - } - - } - - /** - * Checks validation of `spBeneficiary` [Spinner].

- * Disables `spBeneficiary` [Spinner] and sets `pvTwo` to completed and make - * `pvThree` active - */ - private fun payToSelected() { - - with(binding) { - if (spBeneficiary.selectedItem.toString() == binding.spPayFrom.selectedItem.toString()) { - showToaster(getString(R.string.error_same_account_transfer)) - return - } - processTwo.setCurrentCompleted() - processThree.setCurrentActive() - btnPayTo.visibility = View.GONE - tvSelectAmount.visibility = View.GONE - etAmount.visibility = View.VISIBLE - btnAmount.visibility = View.VISIBLE - spBeneficiary.isEnabled = false - } - - } - - /** - * Checks validation of `etAmount` [EditText].

- * Disables `etAmount` and sets `pvThree` to completed and make - * `pvFour` active - */ - private fun amountSet() { - with(binding) { - if (etAmount.text.toString() == "") { - showToaster(getString(R.string.enter_amount)) - return - } - if (etAmount.text.toString() == ".") { - showToaster(getString(R.string.invalid_amount)) - return - } - if (etAmount.text.toString().toDouble() == 0.0) { - showToaster(getString(R.string.amount_greater_than_zero)) - return - } - processThree.setCurrentCompleted() - processFour.setCurrentActive() - btnAmount.visibility = View.GONE - tvEnterRemark.visibility = View.GONE - etRemark.visibility = View.VISIBLE - llReview.visibility = View.VISIBLE - etAmount.isEnabled = false - } - - } - - private fun cancelTransfer() { - activity?.supportFragmentManager?.popBackStack() - } - - private fun addBeneficiary() { - (activity as BaseActivity?)?.replaceFragment( - BeneficiaryAddOptionsFragment.newInstance(), - true, - R.id.container, - ) - } - - private fun onRetry() { - if (Network.isConnected(context)) { - sweetUIErrorHandler?.hideSweetErrorLayoutUI( - binding.llMakeTransfer, - binding.layoutError.root, - ) - viewModel.loadTransferTemplate() - } else { - Toaster.show(binding.root, getString(R.string.internet_not_connected)) - } - } - - /** - * It is called whenever any error occurs while executing a request - * - * @param msg Error message that tells the user about the problem. - */ - fun showError(msg: String?) { - if (!Network.isConnected(context)) { - sweetUIErrorHandler?.showSweetNoInternetUI( - binding.llMakeTransfer, - binding.layoutError.root, - ) - } else { - sweetUIErrorHandler?.showSweetErrorUI( - msg, - binding.llMakeTransfer, - binding.layoutError.root, - ) - } - } - - fun showProgress() { - binding.llMakeTransfer.visibility = View.GONE - showProgressBar() - } - - fun hideProgress() { - hideProgressBar() - binding.llMakeTransfer.visibility = View.VISIBLE - } - - /** - * Callback for `spPayFrom` and `spBeneficiary` - */ - override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - when (parent?.id) { - R.id.sp_beneficiary -> - beneficiaryAccountOption = - viewModel.searchAccount( - accountOptionsTemplate?.toAccountOptions, - beneficiaryAdapter?.getItem(position), - ) - - R.id.sp_pay_from -> - fromAccountOption = - accountOptionsTemplate?.fromAccountOptions?.get(position) - } - } - - override fun onNothingSelected(parent: AdapterView<*>?) {} - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - super.onCreateOptionsMenu(menu, inflater) - inflater.inflate(R.menu.menu_transfer, menu) - Utils.setToolbarIconColor(activity, menu, R.color.white) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == R.id.menu_refresh_transfer) { - val transaction = fragmentManager?.beginTransaction() - val currFragment = activity?.supportFragmentManager - ?.findFragmentById(R.id.container) - if (currFragment != null) { - transaction?.detach(currFragment) - transaction?.attach(currFragment) - } - transaction?.commit() - } - return super.onOptionsItemSelected(item) - } - - override fun onDestroyView() { - super.onDestroyView() - hideProgress() - _binding = null - } - - companion object { - fun newInstance(): ThirdPartyTransferFragment { - return ThirdPartyTransferFragment() - } - } -} - - */ diff --git a/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessComposeFragment.kt index 686ee9cd51..4913431d69 100644 --- a/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessComposeFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessComposeFragment.kt @@ -14,6 +14,8 @@ import org.mifos.mobile.core.common.Constants import org.mifos.mobile.core.model.entity.payload.TransferPayload import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedParcelable import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedSerializable +import org.mifos.mobile.feature.transfer.process.TransferProcessScreen +import org.mifos.mobile.feature.transfer.process.TransferProcessViewModel /** * Created by dilpreet on 1/7/17. diff --git a/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessFragment.kt b/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessFragment.kt deleted file mode 100644 index 02b381cb4d..0000000000 --- a/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessFragment.kt +++ /dev/null @@ -1,231 +0,0 @@ -package org.mifos.mobile.ui.transfer_process - -/* -import android.graphics.drawable.Animatable -//import android.os.Bundle -//import android.view.LayoutInflater -//import android.view.View -//import android.view.ViewGroup -//import androidx.fragment.app.viewModels -//import androidx.lifecycle.Lifecycle -//import androidx.lifecycle.lifecycleScope -//import androidx.lifecycle.repeatOnLifecycle -import dagger.hilt.android.AndroidEntryPoint -import org.mifos.mobile.databinding.FragmentTransferProcessBinding -import kotlinx.coroutines.launch -import org.mifos.mobile.R -import org.mifos.mobile.databinding.FragmentTransferProcessBinding -import org.mifos.mobile.models.payload.TransferPayload -import org.mifos.mobile.models.templates.account.AccountOption -import org.mifos.mobile.ui.activities.SavingsAccountContainerActivity -import org.mifos.mobile.ui.enums.TransferType -import org.mifos.mobile.ui.fragments.base.BaseFragment -import org.mifos.mobile.core.common.Constants -import org.mifos.mobile.core.common.utils.CurrencyUtil -import org.mifos.mobile.core.common.utils.DateHelper -import org.mifos.mobile.utils.MFErrorParser -import org.mifos.mobile.core.common.Network -import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedParcelable -import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedSerializable -import org.mifos.mobile.utils.Toaster -import org.mifos.mobile.utils.TransferUiState -import org.mifos.mobile.utils.getTodayFormatted -import org.mifos.mobile.viewModels.TransferProcessViewModel - -/** - * Created by dilpreet on 1/7/17. - */ -@AndroidEntryPoint -class TransferProcessFragment : BaseFragment() { - - private var _binding: FragmentTransferProcessBinding? = null - private val binding get() = _binding!! - - private val viewModel: TransferProcessViewModel by viewModels() - - private var toAccountOption: AccountOption? = null - private var fromAccountOption: AccountOption? = null - - private var payload: TransferPayload? = null - private var transferType: TransferType? = null - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - if (activity != null) { - payload = arguments?.getParcelable(Constants.PAYLOAD) - payload = - arguments?.getCheckedParcelable( - TransferPayload::class.java, - Constants.PAYLOAD - ) - transferType = arguments?.getCheckedSerializable( - TransferType::class.java, - Constants.TRANSFER_TYPE - ) as TransferType } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle?, - ): View { - _binding = FragmentTransferProcessBinding.inflate(inflater, container, false) - setToolbarTitle(getString(R.string.transfer)) - with(binding) { - tvAmount.text = CurrencyUtil.formatCurrency(activity, payload?.transferAmount) - tvPayFrom.text = payload?.fromAccountNumber.toString() - tvPayTo.text = payload?.toAccountNumber.toString() - tvDate.text = payload?.transferDate - tvRemark.text = payload?.transferDescription - } - - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.transferUiState.collect { - when (it) { - is TransferUiState.Loading -> showProgress() - - is TransferUiState.TransferSuccess -> { - hideProgress() - showTransferredSuccessfully() - } - - is TransferUiState.Error -> { - hideProgress() - showError(MFErrorParser.errorMessage(it.errorMessage)) - } - - TransferUiState.Initial -> {} - } - } - } - } - - with(binding) { - btnStartTransfer.setOnClickListener { - startTransfer() - } - btnCancelTransfer.setOnClickListener { - cancelTransferProcess() - } - btnClose.setOnClickListener { - closeClicked() - } - } - } - - /** - * Initiates a transfer depending upon `transferType` - */ - private fun startTransfer() { - if (!Network.isConnected(activity)) { - Toaster.show(binding.root, getString(R.string.internet_not_connected)) - return - } - viewModel.makeTransfer( - fromAccountOption?.officeId, - fromAccountOption?.clientId, - fromAccountOption?.accountType?.id, - fromAccountOption?.accountId, - toAccountOption?.officeId, - toAccountOption?.clientId, - toAccountOption?.accountType?.id, - toAccountOption?.accountId, - DateHelper.getSpecificFormat( - DateHelper.FORMAT_dd_MMMM_yyyy, - getTodayFormatted(), - ), - binding.tvAmount.text.toString().toDouble(), - binding.tvRemark.text.toString(), - "dd MMMM yyyy", - "en", - fromAccountOption?.accountNo, - toAccountOption?.accountNo, - transferType - ) - - } - - /** - * Cancels the Transfer and pops fragment - */ - fun cancelTransferProcess() { - Toaster.cancelTransfer( - binding.root, - getString(R.string.cancel_transfer), - getString(R.string.yes), - View.OnClickListener { - activity?.supportFragmentManager?.popBackStack() - activity?.supportFragmentManager?.popBackStack() - }, - ) - } - - /** - * Closes the transfer fragment - */ - private fun closeClicked() { - activity?.supportFragmentManager?.popBackStack() - activity?.supportFragmentManager?.popBackStack() - } - - /** - * Shows a {@link Snackbar} on succesfull transfer of money - */ - private fun showTransferredSuccessfully() { - Toaster.show(binding.root, getString(R.string.transferred_successfully)) - binding.ivSuccess.visibility = View.VISIBLE - (binding.ivSuccess.drawable as Animatable).start() - binding.btnClose.visibility = View.VISIBLE - binding.llTransfer.visibility = View.GONE - SavingsAccountContainerActivity.transferSuccess = true - } - - /** - * It is called whenever any error occurs while executing a request - * - * @param msg Error message that tells the user about the problem. - */ - fun showError(msg: String?) { - Toaster.show(binding.root, msg) - } - - fun showProgress() { - showMifosProgressDialog(getString(R.string.please_wait)) - } - - fun hideProgress() { - hideMifosProgressDialog() - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - - companion object { - /** - * Used for TPT Transfer and own Account Transfer.

- * Use `type` as TransferType.TPT for TPT and TransferType.SELF for self Account Transfer - * - * @param payload Transfer Information - * @param type enum of [TransferType] - * @return Instance of [TransferProcessFragment] - */ - fun newInstance(payload: TransferPayload?, type: TransferType?): TransferProcessFragment { - val fragment = TransferProcessFragment() - val args = Bundle() - args.putParcelable(Constants.PAYLOAD, payload) - args.putSerializable(Constants.TRANSFER_TYPE, type) - fragment.arguments = args - return fragment - } - } -} - - */ diff --git a/app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordFragment.kt b/app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordFragment.kt index 46f78a54ea..c146c050f2 100644 --- a/app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordFragment.kt @@ -4,10 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.ViewCompositionStrategy import dagger.hilt.android.AndroidEntryPoint -import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.core.ui.component.mifosComposeView +import org.mifos.mobile.feature.update_password.UpdatePasswordScreen import org.mifos.mobile.ui.activities.base.BaseActivity import org.mifos.mobile.ui.fragments.base.BaseFragment @@ -22,17 +21,12 @@ class UpdatePasswordFragment : BaseFragment() { container: ViewGroup?, savedInstanceState: Bundle?, ): View { - return ComposeView(requireContext()).apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - MifosMobileTheme { - UpdatePasswordScreen( - navigateBack = { - requireActivity().onBackPressedDispatcher.onBackPressed() - } - ) + return mifosComposeView(requireContext()) { + UpdatePasswordScreen( + navigateBack = { + requireActivity().onBackPressedDispatcher.onBackPressed() } - } + ) } } diff --git a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileActivity.kt b/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileActivity.kt index 1faad10617..eeb400596d 100644 --- a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileActivity.kt +++ b/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileActivity.kt @@ -13,6 +13,6 @@ class UserProfileActivity : BaseActivity() { super.onCreate(savedInstanceState) binding = ActivityUserProfileBinding.inflate(layoutInflater) setContentView(binding.root) - replaceFragment(UserProfileFragment.newInstance(), false, R.id.container) + replaceFragment(UserProfileComposeFragment.newInstance(), false, R.id.container) } } diff --git a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileComposeFragment.kt b/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileComposeFragment.kt new file mode 100644 index 0000000000..451c2e0599 --- /dev/null +++ b/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileComposeFragment.kt @@ -0,0 +1,95 @@ +package org.mifos.mobile.ui.user_profile + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.fragment.app.viewModels +import dagger.hilt.android.AndroidEntryPoint +import org.mifos.mobile.R +import org.mifos.mobile.core.common.Constants.USER_DETAILS +import org.mifos.mobile.ui.activities.base.BaseActivity +import org.mifos.mobile.ui.fragments.base.BaseFragment +import org.mifos.mobile.ui.update_password.UpdatePasswordFragment +import org.mifos.mobile.core.datastore.PreferencesHelper +import org.mifos.mobile.core.model.entity.client.Client +import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.core.common.utils.ParcelableAndSerializableUtils.getCheckedParcelable +import org.mifos.mobile.feature.user_profile.screens.UserProfileScreen +import org.mifos.mobile.feature.user_profile.viewmodel.UserDetailViewModel +import javax.inject.Inject + +/** + * Created by dilpreet on 10/7/17. + */ +@AndroidEntryPoint +class UserProfileComposeFragment : BaseFragment() { + + private val viewModel: UserDetailViewModel by viewModels() + + @JvmField + @Inject + var preferencesHelper: PreferencesHelper? = null + private var client: Client? = null + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + (activity as BaseActivity?)?.supportActionBar?.setDisplayHomeAsUpEnabled(true) + if (savedInstanceState == null) { + viewModel.userDetails + viewModel.userImage + } + + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + MifosMobileTheme { + UserProfileScreen( + navigateBack = { backToHome() }, + changePassword = { changePassword() } + ) + } + } + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putParcelable(USER_DETAILS, client) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + if (savedInstanceState != null) { + client = savedInstanceState.getCheckedParcelable( + Client::class.java, + USER_DETAILS + ) + viewModel.setUserProfile(preferencesHelper?.userProfileImage) + } + } + + private fun changePassword() { + (activity as BaseActivity?)?.replaceFragment( + UpdatePasswordFragment.newInstance(), + true, + R.id.container, + ) + } + + private fun backToHome() { + requireActivity().onBackPressedDispatcher.onBackPressed() + } + + companion object { + @JvmStatic + fun newInstance(): UserProfileComposeFragment { + return UserProfileComposeFragment() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileFragment.kt b/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileFragment.kt index 4d5a971dda..4115b3ca8d 100644 --- a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileFragment.kt +++ b/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileFragment.kt @@ -1,5 +1,6 @@ package org.mifos.mobile.ui.user_profile +/* import android.graphics.Bitmap import android.os.Bundle import android.view.LayoutInflater @@ -263,3 +264,4 @@ class UserProfileFragment : BaseFragment() { } } } +*/ \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileScreen.kt b/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileScreen.kt deleted file mode 100644 index 56f9cd9096..0000000000 --- a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileScreen.kt +++ /dev/null @@ -1,105 +0,0 @@ -package org.mifos.mobile.ui.user_profile - -import android.content.res.Configuration -import android.graphics.Bitmap -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Divider -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import org.mifos.mobile.R -import org.mifos.mobile.core.ui.component.MifosUserImage -import org.mifos.mobile.core.ui.component.NoInternet -import org.mifos.mobile.core.ui.component.UserProfileField -import org.mifos.mobile.core.ui.component.UserProfileTopBar - -/** - * @author pratyush - * @since 20/12/2023 - */ - -@Composable -fun UserProfileScreen( - userDetails: UserDetails, - changePassword: () -> Unit, - isOnline: Boolean, - bitmap: Bitmap, - home: () -> Unit -) { - - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) { - UserProfileTopBar(home = home, text = R.string.user_details) - - if (!isOnline) { - NoInternet( - icon = R.drawable.ic_error_black_24dp, - error = R.string.error_fetching_user_profile - ) - return - } - - Row( - modifier = Modifier - .fillMaxWidth() - .padding(top = 100.dp, bottom = 20.dp), - horizontalArrangement = Arrangement.Center - ) { - MifosUserImage( - bitmap = bitmap, - modifier = Modifier.size(100.dp) - ) - } - Divider(color = Color(0xFF8E9099)) - userDetails.userName?.let { UserProfileField(label = R.string.username, value = it) } - userDetails.accountNumber?.let { - UserProfileField( - label = R.string.account_number, value = it - ) - } - userDetails.activationDate?.let { - UserProfileField( - label = R.string.activation_date, value = it - ) - } - userDetails.officeName?.let { UserProfileField(label = R.string.office_name, value = it) } - userDetails.groups?.let { UserProfileField(label = R.string.groups, value = it) } - userDetails.clientType?.let { UserProfileField(label = R.string.client_type, value = it) } - userDetails.clientClassification?.let { - UserProfileField( - label = R.string.client_classification, value = it - ) - } - UserProfileField(text = R.string.change_password, - icon = R.drawable.ic_keyboard_arrow_right_black_24dp, - onClick = { changePassword.invoke() }) - - UserProfileDetails(userDetails = userDetails) - } -} - -@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO) -@Composable -fun UserProfileScreenPreview() { - UserProfileScreen( - UserDetails(), - {}, - true, - Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888), - {} - ) -} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/widgets/ChargeWidgetDataProvider.kt b/app/src/main/java/org/mifos/mobile/ui/widgets/ChargeWidgetDataProvider.kt index ee67b59a4e..1daba1a5d0 100644 --- a/app/src/main/java/org/mifos/mobile/ui/widgets/ChargeWidgetDataProvider.kt +++ b/app/src/main/java/org/mifos/mobile/ui/widgets/ChargeWidgetDataProvider.kt @@ -8,7 +8,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import org.mifos.mobile.R import org.mifos.mobile.core.data.repositories.ClientChargeRepository import org.mifos.mobile.core.datastore.model.Charge -import org.mifos.mobile.ui.client_charge.ClientChargeViewModel +import org.mifos.mobile.feature.client_charge.viewmodel.ClientChargeViewModel import java.util.concurrent.locks.ReentrantLock import javax.inject.Inject @@ -22,14 +22,17 @@ class ChargeWidgetDataProvider(@param:ApplicationContext private val context: Co @Inject lateinit var clientChargeRepository: ClientChargeRepository - private lateinit var clientChargeViewModel: ClientChargeViewModel + private lateinit var clientChargeViewModel: org.mifos.mobile.feature.client_charge.viewmodel.ClientChargeViewModel private var charges: List? private val `object`: ReentrantLock = ReentrantLock() private val condition = `object`.newCondition() override fun onCreate() { - clientChargeViewModel = ClientChargeViewModel(clientChargeRepository) + clientChargeViewModel = + ClientChargeViewModel( + clientChargeRepository + ) } override fun onDataSetChanged() { diff --git a/app/src/main/java/org/mifos/mobile/utils/HelpUiState.kt b/app/src/main/java/org/mifos/mobile/utils/HelpUiState.kt index c2c2852b96..70b55070b4 100644 --- a/app/src/main/java/org/mifos/mobile/utils/HelpUiState.kt +++ b/app/src/main/java/org/mifos/mobile/utils/HelpUiState.kt @@ -2,10 +2,3 @@ package org.mifos.mobile.utils import org.mifos.mobile.core.model.entity.FAQ -sealed class HelpUiState { - object Initial : HelpUiState() - data class ShowFaq( - val faqArrayList: ArrayList, - val selectedFaqPosition: Int = -1 - ) : HelpUiState() -} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/utils/LoginUiState.kt b/app/src/main/java/org/mifos/mobile/utils/LoginUiState.kt deleted file mode 100644 index eea5d04c71..0000000000 --- a/app/src/main/java/org/mifos/mobile/utils/LoginUiState.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.mifos.mobile.utils - -sealed class LoginUiState { - object Initial : LoginUiState() - object LoginSuccess : LoginUiState() - object Loading : LoginUiState() - object Error : LoginUiState() - data class LoadClientSuccess(val clientName: String?) : LoginUiState() -} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/utils/StatusUtils.kt b/app/src/main/java/org/mifos/mobile/utils/StatusUtils.kt index 355ea14e82..93581dd9c1 100644 --- a/app/src/main/java/org/mifos/mobile/utils/StatusUtils.kt +++ b/app/src/main/java/org/mifos/mobile/utils/StatusUtils.kt @@ -11,120 +11,6 @@ import org.mifos.mobile.ui.getThemeAttributeColor */ object StatusUtils { - fun getSavingsAccountStatusList(context: Context?): List { - val arrayList = ArrayList() - arrayList.add( - CheckboxStatus( - context?.getString(R.string.active), - ContextCompat.getColor(context!!, R.color.deposit_green), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.approved), - ContextCompat.getColor(context, R.color.light_green), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.approval_pending), - ContextCompat - .getColor(context, R.color.light_yellow), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.matured), - ContextCompat.getColor(context, R.color.red_light), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.closed), - context.getThemeAttributeColor(R.attr.colorOnSurface), - ), - ) - return arrayList - } - - fun getLoanAccountStatusList(context: Context?): List { - val arrayList = ArrayList() - arrayList.add( - CheckboxStatus( - context?.getString(R.string.in_arrears), - ContextCompat.getColor(context!!, R.color.red), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.active), - ContextCompat.getColor(context, R.color.deposit_green), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.waiting_for_disburse), - ContextCompat.getColor(context, R.color.blue), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.approval_pending), - ContextCompat - .getColor(context, R.color.light_yellow), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.overpaid), - ContextCompat.getColor(context, R.color.purple), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.closed), - context.getThemeAttributeColor(R.attr.colorOnSurface), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.withdrawn), - context.getThemeAttributeColor(R.attr.colorOnSurfaceVariant), - ), - ) - return arrayList - } - - fun getShareAccountStatusList(context: Context?): List { - val arrayList = ArrayList() - arrayList.add( - CheckboxStatus( - context?.getString(R.string.active), - ContextCompat.getColor(context!!, R.color.deposit_green), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.approved), - ContextCompat.getColor(context, R.color.light_green), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.approval_pending), - ContextCompat - .getColor(context, R.color.light_yellow), - ), - ) - arrayList.add( - CheckboxStatus( - context.getString(R.string.closed), - ContextCompat.getColor(context, R.color.light_blue), - ), - ) - return arrayList - } - fun getSavingsAccountTransactionList(context: Context?): List { val arrayList = ArrayList() arrayList.add( diff --git a/app/src/main/java/org/mifos/mobile/utils/UserDetailUiState.kt b/app/src/main/java/org/mifos/mobile/utils/UserDetailUiState.kt deleted file mode 100644 index 5375a6ca22..0000000000 --- a/app/src/main/java/org/mifos/mobile/utils/UserDetailUiState.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.mifos.mobile.utils - -import android.graphics.Bitmap -import org.mifos.mobile.core.model.entity.client.Client - - -sealed class UserDetailUiState { - object Loading : UserDetailUiState() - data class ShowError(val message: Int) : UserDetailUiState() - data class ShowUserDetails(val client: Client) : UserDetailUiState() - data class ShowUserImage(val image: Bitmap?) : UserDetailUiState() -} diff --git a/app/src/main/res/layout/fragment_locations.xml b/app/src/main/res/layout/fragment_locations.xml index bf596d584f..efca737f0d 100644 --- a/app/src/main/res/layout/fragment_locations.xml +++ b/app/src/main/res/layout/fragment_locations.xml @@ -21,11 +21,5 @@ android:layout_height="wrap_content" android:text="@string/mifos_location" /> - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c8fad4d805..22efeb4f04 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -158,9 +158,6 @@ Change Passcode Change App Passcode Change Password - Change your Account Password - Current Password - New Password %1$s changed successfully Account should to be Active to perform a @@ -513,40 +510,6 @@ Scan your fingerprint - - How do I apply for new loan account? - Where can I view my profile information? - Where can I see my Savings Accounts Transactions? - What is the use of QR Code? - How to create a beneficiary using QR Code? - How to make a payment for a Loan Account? - - - - To apply for loan account, click on \"Apply for Loan\" given on the Home Screen. - - You can view your profile information by clicking on the User Image present on Home - Screen. - - To view your savings account transactions, navigate to the Accounts Sections, click on - the required savings account, click on three dots present on top right and select - Transactions option. - - QrCode for any loan or savings accounts can be shared with other users which will - allow them to create a beneficiary. - - In order to create a Beneficiary, goto Beneficiary option from Home Screen then click - on the circular button present on bottom right, choose the option to Scan which opens - your device camera, scan the QR Code of other person for which you want to create a - beneficiary, after filling in the other required detail you will be able to create a - Beneficiary using QR Code. - - To make a payment for a loan account, navigate to the Accounts Sections, choose LOAN - then open your required Loan Account and click on Make Payment option. - - - - language_type default_system_language theme_type @@ -664,7 +627,6 @@ App Info Login Failed, Please Try Again Later. - We were unable to update password. Password changed successfully We were unable to register the user. No Questions Found diff --git a/app/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt index 7831dee802..a403b66fc6 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/AccountsViewModelTest.kt @@ -11,12 +11,12 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mifos.mobile.models.client.ClientAccounts -import org.mifos.mobile.repositories.AccountsRepository -import org.mifos.mobile.repositories.HomeRepositoryImp -import org.mifos.mobile.ui.account.AccountsViewModel +import org.mifos.mobile.core.data.repositories.AccountsRepository +import org.mifos.mobile.core.data.repositories.HomeRepositoryImp +import org.mifos.mobile.core.model.entity.client.ClientAccounts +import org.mifos.mobile.feature.account.utils.AccountState +import org.mifos.mobile.feature.account.viewmodel.AccountsViewModel import org.mifos.mobile.util.RxSchedulersOverrideRule -import org.mifos.mobile.utils.AccountsUiState import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito @@ -51,7 +51,10 @@ class AccountsViewModelTest { @Before fun setUp() { MockitoAnnotations.openMocks(this) - accountsViewModel = AccountsViewModel(accountsRepositoryImp, homeRepositoryImp) + accountsViewModel = org.mifos.mobile.feature.account.viewmodel.AccountsViewModel( + accountsRepositoryImp, + homeRepositoryImp + ) mockClientAccounts = Mockito.mock(ClientAccounts::class.java) } @@ -61,15 +64,15 @@ class AccountsViewModelTest { `when`(homeRepositoryImp.clientAccounts()).thenReturn(flowOf(mockClientAccounts)) accountsViewModel.accountsUiState.test { accountsViewModel.loadClientAccounts() - assertEquals(AccountsUiState.Loading, awaitItem()) + assertEquals(AccountState.Loading, awaitItem()) assertEquals( - AccountsUiState.ShowSavingsAccounts(mockClientAccounts.savingsAccounts), awaitItem() + AccountState.ShowSavingsAccounts(mockClientAccounts.savingsAccounts), awaitItem() ) assertEquals( - AccountsUiState.ShowLoanAccounts(mockClientAccounts.loanAccounts), awaitItem() + AccountState.ShowLoanAccounts(mockClientAccounts.loanAccounts), awaitItem() ) assertEquals( - AccountsUiState.ShowShareAccounts(mockClientAccounts.shareAccounts), awaitItem() + AccountState.ShowShareAccounts(mockClientAccounts.shareAccounts), awaitItem() ) cancelAndIgnoreRemainingEvents() } @@ -86,9 +89,9 @@ class AccountsViewModelTest { ) accountsViewModel.accountsUiState.test { accountsViewModel.loadAccounts(mockAccountType) - assertEquals(AccountsUiState.Loading, awaitItem()) + assertEquals(AccountState.Loading, awaitItem()) assertEquals( - AccountsUiState.ShowSavingsAccounts(mockClientAccounts.savingsAccounts), awaitItem() + AccountState.ShowSavingsAccounts(mockClientAccounts.savingsAccounts), awaitItem() ) cancelAndIgnoreRemainingEvents() } @@ -101,9 +104,9 @@ class AccountsViewModelTest { accountsViewModel.accountsUiState.test { try { accountsViewModel.loadAccounts(mockAccountType) - assertEquals(AccountsUiState.Loading, awaitItem()) + assertEquals(AccountState.Loading, awaitItem()) } catch (e: RuntimeException) { - assertEquals(AccountsUiState.Error, awaitItem()) + assertEquals(AccountState.Error, awaitItem()) } cancelAndIgnoreRemainingEvents() } diff --git a/app/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt index e25b8089e8..fbaf4d85eb 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/ClientChargeViewModelTest.kt @@ -17,7 +17,7 @@ import org.mifos.mobile.R import org.mifos.mobile.models.Charge import org.mifos.mobile.models.Page import org.mifos.mobile.repositories.ClientChargeRepositoryImp -import org.mifos.mobile.ui.client_charge.ClientChargeViewModel +import org.mifos.mobile.feature.client_charge.viewmodel.ClientChargeViewModel import org.mifos.mobile.util.RxSchedulersOverrideRule import org.mifos.mobile.utils.ClientChargeUiState import org.mockito.Mock @@ -45,12 +45,14 @@ class ClientChargeViewModelTest { @Mock private lateinit var clientChargeUiStateObserver: Observer - private lateinit var viewModel: ClientChargeViewModel + private lateinit var viewModel: org.mifos.mobile.feature.client_charge.viewmodel.ClientChargeViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) - viewModel = ClientChargeViewModel(clientChargeRepositoryImp) + viewModel = org.mifos.mobile.feature.client_charge.viewmodel.ClientChargeViewModel( + clientChargeRepositoryImp + ) } @Test diff --git a/app/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt index 1ff142ada6..b91231e5da 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/HelpViewModelTest.kt @@ -1,7 +1,6 @@ package org.mifos.mobile.viewModels import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.lifecycle.Observer import app.cash.turbine.test import junit.framework.Assert.assertEquals import kotlinx.coroutines.test.advanceUntilIdle @@ -11,7 +10,6 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mifos.mobile.models.FAQ -import org.mifos.mobile.ui.help.HelpViewModel import org.mifos.mobile.utils.HelpUiState import org.mockito.Mock import org.mockito.Mockito.* diff --git a/app/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt index 5776fca970..dad282b7f0 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/LoginViewModelTest.kt @@ -10,14 +10,15 @@ import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.setMain import org.junit.* import org.junit.runner.RunWith -import com.mifos.mobile.core.data.utils.FakeRemoteDataSource -import org.mifos.mobile.models.Page -import org.mifos.mobile.models.User -import org.mifos.mobile.models.client.Client -import org.mifos.mobile.repositories.ClientRepository -import org.mifos.mobile.repositories.UserAuthRepository -import org.mifos.mobile.ui.login.LoginViewModel +import org.mifos.mobile.core.data.repositories.ClientRepository +import org.mifos.mobile.core.data.repositories.UserAuthRepository +import org.mifos.mobile.core.model.entity.Page +import org.mifos.mobile.core.model.entity.User +import org.mifos.mobile.core.model.entity.client.Client +import org.mifos.mobile.feature.login.utils.LoginState +import org.mifos.mobile.feature.login.viewmodel.LoginViewModel import org.mifos.mobile.util.RxSchedulersOverrideRule +import org.mifos.mobile.utils.FakeRemoteDataSource import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock @@ -48,10 +49,13 @@ class LoginViewModelTest { @Before fun setUp() { MockitoAnnotations.openMocks(this) - loginViewModel = LoginViewModel(userAuthRepositoryImp, clientRepositoryImp) - mockUser = com.mifos.mobile.core.data.utils.FakeRemoteDataSource.user - emptyClientPage = com.mifos.mobile.core.data.utils.FakeRemoteDataSource.noClients - clientPage = com.mifos.mobile.core.data.utils.FakeRemoteDataSource.clients + loginViewModel = LoginViewModel( + userAuthRepositoryImp, + clientRepositoryImp + ) + mockUser = FakeRemoteDataSource.user + emptyClientPage = FakeRemoteDataSource.noClients + clientPage = FakeRemoteDataSource.clients } @Test @@ -110,8 +114,8 @@ class LoginViewModelTest { ).thenReturn(flowOf(mockUser)) loginViewModel.loginUiState.test { loginViewModel.login("username", "password") - Assert.assertEquals(LoginUiState.Initial, awaitItem()) - Assert.assertEquals(LoginUiState.LoginSuccess, awaitItem()) + Assert.assertEquals(LoginState.Initial, awaitItem()) + Assert.assertEquals(LoginState.LoginSuccess, awaitItem()) cancelAndIgnoreRemainingEvents() } Dispatchers.resetMain() @@ -125,8 +129,8 @@ class LoginViewModelTest { ).thenThrow(Exception("Error occurred")) loginViewModel.loginUiState.test { loginViewModel.login("username", "password") - Assert.assertEquals(LoginUiState.Initial, awaitItem()) - Assert.assertEquals(LoginUiState.Error, awaitItem()) + Assert.assertEquals(LoginState.Initial, awaitItem()) + Assert.assertEquals(LoginState.Error, awaitItem()) cancelAndIgnoreRemainingEvents() } Dispatchers.resetMain() @@ -140,9 +144,9 @@ class LoginViewModelTest { ).thenThrow(Exception("Error occurred")) loginViewModel.loginUiState.test { loginViewModel.loadClient() - Assert.assertEquals(LoginUiState.Initial, awaitItem()) - Assert.assertEquals(LoginUiState.Loading, awaitItem()) - Assert.assertEquals(LoginUiState.Error, awaitItem()) + Assert.assertEquals(LoginState.Initial, awaitItem()) + Assert.assertEquals(LoginState.Loading, awaitItem()) + Assert.assertEquals(LoginState.Error, awaitItem()) cancelAndIgnoreRemainingEvents() } Mockito.verify(clientRepositoryImp).clearPrefHelper() @@ -158,9 +162,9 @@ class LoginViewModelTest { ).thenThrow(Exception("Error occurred")) loginViewModel.loginUiState.test { loginViewModel.loadClient() - Assert.assertEquals(LoginUiState.Initial, awaitItem()) - Assert.assertEquals(LoginUiState.Loading, awaitItem()) - Assert.assertEquals(LoginUiState.Error, awaitItem()) + Assert.assertEquals(LoginState.Initial, awaitItem()) + Assert.assertEquals(LoginState.Loading, awaitItem()) + Assert.assertEquals(LoginState.Error, awaitItem()) cancelAndIgnoreRemainingEvents() } Dispatchers.resetMain() @@ -188,8 +192,8 @@ class LoginViewModelTest { ) loginViewModel.loginUiState.test { loginViewModel.loadClient() - Assert.assertEquals(LoginUiState.Initial, awaitItem()) - Assert.assertEquals(LoginUiState.LoadClientSuccess(clientName), awaitItem()) + Assert.assertEquals(LoginState.Initial, awaitItem()) + Assert.assertEquals(LoginState.LoadClientSuccess(clientName), awaitItem()) cancelAndIgnoreRemainingEvents() } Dispatchers.resetMain() diff --git a/app/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt index cecd058d6f..1b15154a09 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/RecentTransactionViewModelTest.kt @@ -10,14 +10,13 @@ import kotlinx.coroutines.test.runTest import org.junit.* import org.junit.runner.RunWith import org.mifos.mobile.R -import org.mifos.mobile.models.Page -import org.mifos.mobile.models.Transaction -import org.mifos.mobile.models.client.Currency -import org.mifos.mobile.models.client.Type -import org.mifos.mobile.repositories.RecentTransactionRepository -import org.mifos.mobile.ui.recent_transactions.RecentTransactionViewModel +import org.mifos.mobile.core.data.repositories.RecentTransactionRepository +import org.mifos.mobile.core.model.entity.Currency +import org.mifos.mobile.core.model.entity.Page +import org.mifos.mobile.core.model.entity.Transaction +import org.mifos.mobile.core.model.entity.accounts.loan.calendardata.Type +import org.mifos.mobile.feature.recent_transaction.utils.RecentTransactionState import org.mifos.mobile.util.RxSchedulersOverrideRule -import org.mifos.mobile.utils.RecentTransactionUiState import org.mockito.Mock import org.mockito.Mockito.* import org.mockito.MockitoAnnotations @@ -47,13 +46,16 @@ class RecentTransactionViewModelTest { @Mock lateinit var currency: Currency - lateinit var viewModel: RecentTransactionViewModel + lateinit var viewModel: org.mifos.mobile.feature.recent_transaction.viewmodel.RecentTransactionViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) - viewModel = RecentTransactionViewModel(recentTransactionRepositoryImp) + viewModel = + org.mifos.mobile.feature.recent_transaction.viewmodel.RecentTransactionViewModel( + recentTransactionRepositoryImp + ) } @Test @@ -78,9 +80,9 @@ class RecentTransactionViewModelTest { .thenReturn(flowOf(transactions)) viewModel.recentTransactionUiState.test { viewModel.loadRecentTransactions(loadMore = false, offset) - assertEquals(RecentTransactionUiState.Initial, awaitItem()) - assertEquals(RecentTransactionUiState.Loading, awaitItem()) - assertEquals(transactions.pageItems.let { RecentTransactionUiState.RecentTransactions(it) }, awaitItem()) + assertEquals(RecentTransactionState.Initial, awaitItem()) + assertEquals(RecentTransactionState.Loading, awaitItem()) + assertEquals(transactions.pageItems.let { RecentTransactionState.RecentTransactions(it) }, awaitItem()) } } @@ -106,9 +108,9 @@ class RecentTransactionViewModelTest { .thenReturn(flowOf(transactions)) viewModel.recentTransactionUiState.test { viewModel.loadRecentTransactions(loadMore = false, offset) - assertEquals(RecentTransactionUiState.Initial, awaitItem()) - assertEquals(RecentTransactionUiState.Loading, awaitItem()) - assertEquals(RecentTransactionUiState.EmptyTransaction, awaitItem()) + assertEquals(RecentTransactionState.Initial, awaitItem()) + assertEquals(RecentTransactionState.Loading, awaitItem()) + assertEquals(RecentTransactionState.EmptyTransaction, awaitItem()) cancelAndIgnoreRemainingEvents() } } @@ -136,9 +138,9 @@ class RecentTransactionViewModelTest { viewModel.recentTransactionUiState.test { viewModel.loadRecentTransactions(loadMore = false, offset) - assertEquals(RecentTransactionUiState.Initial, awaitItem()) - assertEquals(RecentTransactionUiState.Loading, awaitItem()) - assertEquals(transactions.pageItems.let { RecentTransactionUiState.RecentTransactions(it) }, awaitItem()) + assertEquals(RecentTransactionState.Initial, awaitItem()) + assertEquals(RecentTransactionState.Loading, awaitItem()) + assertEquals(transactions.pageItems.let { RecentTransactionState.RecentTransactions(it) }, awaitItem()) cancelAndIgnoreRemainingEvents() } } @@ -149,9 +151,9 @@ class RecentTransactionViewModelTest { .thenThrow(Exception("Error occurred")) viewModel.recentTransactionUiState.test { viewModel.loadRecentTransactions(false, 0) - assertEquals(RecentTransactionUiState.Initial, awaitItem()) - assertEquals(RecentTransactionUiState.Loading, awaitItem()) - assertEquals(RecentTransactionUiState.Error(R.string.recent_transactions), awaitItem()) + assertEquals(RecentTransactionState.Initial, awaitItem()) + assertEquals(RecentTransactionState.Loading, awaitItem()) + assertEquals(RecentTransactionState.Error(R.string.recent_transactions), awaitItem()) cancelAndIgnoreRemainingEvents() } } diff --git a/app/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt index ef8491737c..98313c5686 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/UpdatePasswordViewModelTest.kt @@ -12,7 +12,7 @@ import org.junit.* import org.junit.runner.RunWith import org.mifos.mobile.core.data.repositories.ClientRepository import org.mifos.mobile.core.data.repositories.UserAuthRepository -import org.mifos.mobile.ui.update_password.UpdatePasswordViewModel +import org.mifos.mobile.feature.update_password.UpdatePasswordViewModel import org.mifos.mobile.util.RxSchedulersOverrideRule import org.mifos.mobile.feature.registration.utils.RegistrationState import org.mockito.Mock @@ -40,13 +40,16 @@ class UpdatePasswordViewModelTest { @Mock lateinit var clientRepositoryImp: ClientRepository - private lateinit var updatePasswordViewModel: UpdatePasswordViewModel + private lateinit var updatePasswordViewModel: org.mifos.mobile.feature.update_password.UpdatePasswordViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) updatePasswordViewModel = - UpdatePasswordViewModel(userAuthRepositoryImp, clientRepositoryImp) + org.mifos.mobile.feature.update_password.UpdatePasswordViewModel( + userAuthRepositoryImp, + clientRepositoryImp + ) } @Test diff --git a/app/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt b/app/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt index 8c51aa6686..1f1ba38445 100644 --- a/app/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt +++ b/app/src/test/java/org/mifos/mobile/viewModels/UserDetailViewModelTest.kt @@ -3,12 +3,9 @@ package org.mifos.mobile.viewModels import CoroutineTestRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule import app.cash.turbine.test -import junit.framework.Assert import junit.framework.Assert.assertEquals -import junit.framework.Assert.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule @@ -19,9 +16,8 @@ import org.mifos.mobile.api.local.PreferencesHelper import org.mifos.mobile.models.client.Client import org.mifos.mobile.repositories.HomeRepositoryImp import org.mifos.mobile.repositories.UserDetailRepositoryImp -import org.mifos.mobile.ui.user_profile.UserDetailViewModel import org.mifos.mobile.util.RxSchedulersOverrideRule -import org.mifos.mobile.utils.UserDetailUiState +import org.mifos.mobile.feature.user_profile.utils.UserDetailUiState import org.mockito.Mock import org.mockito.Mockito import org.mockito.MockitoAnnotations @@ -51,12 +47,15 @@ class UserDetailViewModelTest { private lateinit var preferencesHelper: PreferencesHelper - private lateinit var viewModel: UserDetailViewModel + private lateinit var viewModel: org.mifos.mobile.feature.user_profile.viewmodel.UserDetailViewModel @Before fun setUp() { MockitoAnnotations.openMocks(this) - viewModel = UserDetailViewModel(userDetailRepositoryImp, homeRepositoryImp) + viewModel = org.mifos.mobile.feature.user_profile.viewmodel.UserDetailViewModel( + userDetailRepositoryImp, + homeRepositoryImp + ) viewModel.preferencesHelper = preferencesHelper } diff --git a/app/src/main/java/org/mifos/mobile/utils/SymbolsUtils.kt b/core/common/src/main/java/org/mifos/mobile/core/common/utils/SymbolsUtils.kt similarity index 70% rename from app/src/main/java/org/mifos/mobile/utils/SymbolsUtils.kt rename to core/common/src/main/java/org/mifos/mobile/core/common/utils/SymbolsUtils.kt index 4096848764..94d46c5b28 100644 --- a/app/src/main/java/org/mifos/mobile/utils/SymbolsUtils.kt +++ b/core/common/src/main/java/org/mifos/mobile/core/common/utils/SymbolsUtils.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.utils +package org.mifos.mobile.core.common.utils /** * Created by Rajan Maurya on 05/03/17. diff --git a/core/common/src/main/res/drawable/circular_background.xml b/core/common/src/main/res/drawable/circular_background.xml index b4f39d2e21..ed806b933b 100644 --- a/core/common/src/main/res/drawable/circular_background.xml +++ b/core/common/src/main/res/drawable/circular_background.xml @@ -2,6 +2,6 @@ + android:width="25dp" + android:height="25dp" /> \ No newline at end of file diff --git a/core/common/src/main/res/mipmap-hdpi/mifos_icon.png b/core/common/src/main/res/mipmap-hdpi/mifos_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe9d645b0bea5dbc7bf260c93cdffa56c5bd5122 GIT binary patch literal 28552 zcmV*_Kq|k9P) zcYqbu-Tpu4%*?IZcUgLo-V{_Upa|Hzi4jaMmKaNn8dKjlS$LDY=}})xVtW0VK#D2$ z0@w>G3W6vdsk>~w{mz{8`(tMAF8$tZ?7}{J+1)XfMTEoh|rPpTNiQ6{aw#ev%PXnugO}G=lYGBu*x8H6MLSznbZ9dU8p!x{jy%2}~NFO?iwgg_pB0W#c zbN2I@VxT{8JTP3LPHsuJ^pDx`@y+Sx(u~Lkbx_fiZl)>IOfs7!kxdX!Cy1q5NM+MF z+CgiLtsER>qqRnB_g{faER7bXFohuvVMqf*8idRcAu~wG2vHO&qAXHMS*V<{U@0ZR z63UEHN+>36rwN&%B(Me82&@N|18aeI1Y#Qkz1MXKT+N}6;N5^eLU$3ofh+-p5YArj zN#GdZB!xPzKG}FueY$>pL#lp2(oCBT*#;Vt4K$=0XoxkE&ZbFaQ(9@|fIvu)LL#*k zQc46uN+bvn-Xj%4zyY4GM&tgkHGt9ztu+dT(n@=i09tD;q`;CE0T~db!BV3vR7U^e zYKE2#p?|bL1I%hHEF>}oyo*cj=W&U?QV6kYKi4klBY2mfkICX@_>(}}31P46LZNV!qaEeoXh$oQmO|=)3{V;EF2P>_UKK)Y-|N_h_iG=yI{|%!?jU>;&tZWLK-^u@DsWu5D0{)+8*8uY6k=YAq0tRf{IW% z7mc`pY5h;!`(wV7PvffDaT^5xk-J^!BXj{+xNxDdh(#=#w5 zYpqAtrfV-=+qCWjo15RCQ7;;TyJEX&j5Xrej#hqA|CV)+R2o4;jKc+R;boLm(%O z=ghI&Y^$ns8qR-v*UArXYS}!eQ8Y$&#cGMgVxWZ`kd}}_N^m9ckpiEn2|Gc*a6c|N z<|3x{I}UIiYf|hjfDZ}fz4n&g+kl6F2Y_dMr=l;l_jWS&p%s0EJ^*TTjhw_J;fI() zMu_@k{j8UFE&If#*yc+bbVK#-)NW!eF@zS5Wmum7`N$%%4=E%WCxg<8=>t#U!V%|F zWEG*bLI`m%shlr->D@ND2H;KLkH8`!#HQSbU8(K#spEU1kI;KTYc1z3nkRg6Jwm#xX3|{z4yHa^zbRd@=a}kp&Cj#x3%K1P84tuKQ zF{RW)Qp#TnA)d^Au#eb#ppVcVwAOMy^YOz?DV-Yg_~s|C+1$M4<1rZ@)R1T-ok(fR zupEgrID$#+!x{BJiu{>)u&-dcO%RkW~u-l*)sP26M?V z7csG7JkX_5`B2bGDJ7+po9$g zvG+N5@eQgxP@=CL{~_bk!!IL%+yVUZ{lxqC4~-6eA7Js~#pdkUvyVDe{HPhIK5A-e ztA$Nm2MMb@x>$G*>?V6`rqMPRIz-oQSoOgDo8q;1 zk0`3%4o9EFK57Om8d0;P##k_CferA}6~CI&kgfY_BAfiM4rtP`w3bMP*uz#kDjFqk_;27cMmB&2z&~KYZ<;;VIGb z)l@cpl?aMJx+SFr;;1CDpY0XTCa8||WZojj z%cwKrkFy4^`W^@q1OZ(Jn(PB#)poSv1JB;Z>kX?ZHlsLt zUqRcJ*v*-^9v&U&+&cN=<|l_<1n`QDU1JIBQ8=Jm=qHx=j_seI3~oB8I0Yp5y@vF5*K=I(et zH@;QX{xdZze@0cH40}IhZVR8-DJM`FC#vwyg3EgYF@ z_z^dtJ*ljP5L#i&0r^}icp8|ImqWKtPNAmq{HwQdV3B#w4S*(s(}9n0mTKR|h~ z+$|0{K;kF0Y@Z_pm0G_=06wVr3k+*dACzmxXo49bvO#pkS+J}Nb zAcZ7uC&{X;x?s?0eCOmZ$RV<-3Ha9RCA05ZI%lbUq&dxwsDZ7uHiZy2fUWJD4UGA9 z>@NTAvp-OVFz}@EQ3*pBB<&C8cTL9qr{5h=)z53>Dk;(nHUi>DeSN=+$@#NL^3r#Cd4O`)*TI=t=u=$0s z5W?|n>t2msFQMDctXk_^fp6n>xwHX8Yb_Ltd*A#S8=5x~GTkJJqXB-IqoWnG2hZff z5f@-dv(u%r(wgr*xq)v#wvJ@lArdwb0^%(hZanK4?)lJ!0vus(V~5?DCeB@a1G_Vg zSkmm2nsML0j@d~ZZL3cnbBXxQ)SHEce4{y=ys0!0dBN}h)>}Q{y_V2^&n|Cf9Rb`2 zT;RR!C-$Q~yKZ}YJ9jR>2aP@|OlT=22|Gb~u$(JKe~`(QlYrLy(Gj$bE5H7JGdKTj zHP5fvjTN>q4S`Y`M`_B!CU1S^GzJt0J#FC7-|J`xLm2$mTlaAP+P_g5C_SVjtWda< znWiD#!sz0m_76_F)jXs8_$;tM2=N{0y()OG)Iqh@QV5|vGG76_f=gzby%PC_5Gbt} z7#+w(BQ79irw&&xa<{-IYb+aQa>XRRF!d%TS59&p0n%|Y+sY-Tqcq=nVm+td`7+P1 zZ6HzH;d*_zyC z&Ru-F`tP^ywSaGHt)JS_xMQR@whT{j^cwW4lLc6)wK31V*%t79;8yRgoK)T)d^PhI zZ~vN=b#D`~B1fF+?N>($A-G`pd7L$L4$wJ{ueKiSx3;x#!=hELRE}7f(j~Zi4v@7K zgUUj@`K1|@g)O)LP#z;qX@x*={QFX$Zq!rD#yLMb{hQ{P;E>uh z_Gd%Fda$>t;Jr#z)hyQLJRzK9QjY=_f;PG{_9iJFoJaAXruxL+3&zI0OS|VVc z5YE`YZvOK3-$^~RA+GuXjw)~|A1NeCD~_$4#O%Rm5X;6edU58bo4%RF)()4Ayo9SK zeX7&Q>`G;qq(6Ip3p4I`i6>Uox>8xP51GLwuF5)marSVc0mE~^9s0ZcbkpB&{0}Re z))6%hPb9+83W=mLP$nPP^q4&JscY?x*&XMODjM*7VD6E(ulPiK6?IJ@$sSkg&e9Wf&ddvbPJ&y~` zWrdn@+M`Bs>2;Zw2D{8cJi8+;fALN;hVa$G{Nd*^o6_W?(l1mle!Kpk;=?a|)mHEY zt@VSy*|0d|dBA(j!09n30&wE}_ghQn%(2HWTt5pd^0*Lkpp%R_v>`KqqoK$YysqNl{FdxQ-= zYq2($&Jp(5Me8p!tmt3RPEgw!g}a>00U?2m)>H%x9z16N1H(pcU6n%M6J%}EdVcoS z&oR74pNCWZ^R|JT**ZcY=4SU9S%D;BuEjMA{h zim%LIKuHjthd;v)Q*DfI=e)(&V`~T5!))h+!4w8DJ4vZkWdHlzyUkJ7pk;5y)?GBU zc=WErodbT@MUhYDF$*_*PzIul&~{MU*+Y^U+!V2(6l_b_eErqkXwM#OuRT4wbR-`f zbs0%J+2Ne$j=*mZgtU6SULMB+_K+mp2{+VVKXW~5tEJUbhXc8C~35>C>*^og78H>`1lCo(7jpT5B#F zc?pv%Clj~3*YoX{JPwQw#@DR7UgnK0%|xOBSH*6pU^Jv4 zo3goJVm~GhD9XF%w)W!Ff86*mJ5%*o(&|;xxh)*62pIts%KX3+x7aIE>ra?aGU1^o z)-5YNT#mBCLg-mFi(NJQfps%QB>H!>3Th{-^HH-81R>mT)|X%1&5nfAZb^-BiS_Yu zA7OBGP`8y^N+F45V@#}=z)e#=NB>B5hskU!x8+Y$%H~slT*3AKZzUN8;ZR|Yul>Q% zAf({>(}x1&H8J=#(T$lF{<8TAqP;LfZ*MqS5tJ5=cFfD4xW(R>-FeE{BTsyA;pP{@ zfbQ<(lWr&U+=bd$x?qkye&N~^%}CL|Ku1*`4zNQ(X$TpDtqGgkUfG?q2@4&z0@oQ8 zwIY0a;wLEz6uFgIU1U?Y_QsAgbI@sAJLxJSX2g?t?YkwKRxkFlEzQil^JRYj!uy1y z)&cXd3xO1ZOxote5oOF7U4hmGgsKqy{r#ue7~4un279e^ZVO*hr&Pwg;PLD29jamW z+@aI|cHL{&nSkt0Qg<(*wU&$K31`#;?+wKa{}U<7R5tC14wBi2qoK@_EZ&~vdv7(i zTT1&)5vs%e_|$}tBM`a4*qQJ>=_xzK#m8L8yfIhgl117(fhbR+Yf?=lz~tWpU_(7B!`TSH|~^0RlE`TNFr zhwN$B^F4}>jJ=YKw}4w`ky;9YqaAEz^O3Pva_+FR(OP$?MUwBuPG%LK{ma{Y_D?Iw z>dsNIeFy<5TQQ(A!n~;ifWoBnQ#)T_Wz$+BW(aizNawb2v_p{@W?Qn>y6ovMWHnr+ zwXW&TDy?oJv=(z0x!)!1#6lxdG|frG>;ol@bQDTMktzB5vU=XE&vcm7Csa@6gQG4Z zVJA8za_@E)$zn>At0#S$QwE&W;T0?VTliNn091E}y~ z*ZG{W^oH7>Ha^^I3SrswI$+^SNA)f7+=iAN6PE#Bx;<{t6 zVPeIET(^$)z%6%lbFu%iViz;-e3_TlHxeoCm>2s%u$9IN8hrYcL3!`vPgX0N*702J z8x)!0UKy&&7q+&k2$hK^x4mrKy8I^!xF?ZG%=AdzrRsOr6MC`h(H{5b_1Brj<+nNU z#%x!T*@vScYzQ{C*xdYL9U0ZWA`WOJ^~EDDVCJCHiDhH$BA^tKq@AQ+xF0th_c=xs z5BD15weK8PDafQ9P9I&))WIdWy6Ho~Ka+)< zA0uWbdv#NZd|@k_>Trel&3pggzV&|%hOO}5wkEa?>*^SF*AjZ6HkdQV9=B-Y3>hxE z(@Dl0lOkM=vhma7zm+7%Kp6(jtq0s38$m6=JvwT zP!^PY|E&fd+!V*`0I3mfGr3Pr_&Ad)CLS_H)DQ+O*%qeso5D4dK10L|w>!q%cD0KC zyj{zzyIyAb`^`j(tWNV{9|(rm&GXX9{iuvux$Y*1f**$a+m0!Enx9|(Lj~N^oNAuf z74~%J6MEr710kHTf8KbS2!{U0Ni;j68w2YHg2q=7HP^pT$C{>WyTe(2Hd(+3@R^C9 zWMt_GPg1wuPAMeKnHbXtp2{c2f2=iOq#Yp}zXPYEG&e3<#chk;CSXYd2Wi^ZnUF$| z$tWfbF6Ok6if)cH*dTh8+lQgM@PNhqeFGi*`O$TpTR^{X)GmKquSN zol9t~1#{=>aSzrO3(NcsQkdGYyWMU+5R`_XA!y1du6eGOZ3(+W*<@O4LPm&dCtuCD z^0CCT?x1y>-p83ecqShiJ1^I(tUYA5y`^P#$Fp4g^Vhg<@kYYY00_FwUfvs0fX+I6 z=(uVuuR7~cSK>Rv5>AT8w!c8w2%A;PQHrqp86fnCOB*8 z9IhC1d50EL_nlww?20q{?pJthMQxYGmk)IwR@mh7nE^FhS;PbW1S=+LOkP+;* z38<$+qcPnOsPU;K|K$K*(psO{nf7$&k!_(i7R?jR_(dBpHX=pWIqz=dcS5N#DW6DQ!#^BSrY~&@bz1TrAFIij9?Eia(m0mt}ci78q zVF*Dc<8bouGA0i!@;Z_n`g;6N{A=q{&z9~3c=mK4DqgQ!CI0u_-)P_$t95NLom83C zIfT|)@HTq*KN`xA)_vN^YUp`Ai`&B1P#%yxyCcQTFV&It)W~*KTzcI^0!Dxvj{PiG zjQ$|t+R=rQS<_19Kfkeq3x4_D(5!vZ)G{%R5&5!C9HvIC4By-}71lcUVey3ZZAsUt+L8sG!~WJtJB?RAo{MdPt|e zgTRkbKfEQuEidhM%Prf{!U$Tu-yL!MXbs9E^N$yA8AM9r{xyYW4~zxHsw{BBUA?-5dhJ3IGib$f5v3aWyV->;4FwGNWHJ@A)YRZ8=j|F@DKJ-Uu?G=T0v zs%mFIxNSYTc%mzv3sJXsBo zce5vZf5<9LC~9%ugessgb^H0+H{0|K>%)N{-IQYEw&#DKfUjw-M*{8cN7jBq*J!!u z?fLrHzikGjk2U5D~ z_K8DFnb5y*b^D2(FCqKX?L)%RiV`a-p53ub|8@7`DDXpXRm*m1x$P$4+)0ZB3l^xf z<18@3MWt>p?Vgc9(h)fOAvNUJ1g{YIrH9*_)0}hft331WZX!j2!{W)_A0&XSIe$Wx z+fcs%b-O;(#7lK62%CMe=X~I3MOmQ4sCnfcr-kI@TI)IO?eE<#LeE{e&{#BYo-=;o zwiAtD_>)eu#Sx-+s@nU5PwL;l*TStY?M7*DxztV?n|{mN^}Catd*3o%-_%4T+Wj%+ zgTYZ6BV=&?cvs!dcj0z`mv*mUd#aW|FK*G>ozQ54GQhS(EqAW{e*izISzKe%4hLDg z2t9G`Tn(T_=DX4gm|8i|%N${cg5!m>{=7cM%`dufYAHIdK)M1;P#eoI>+V-rxuuEX zq5#<*L}n=j>8!)}{zXg~RFvCxXt4HFKXyX{t&C7m=c?_NR32%`JE+XX=i zNm^ya|GxE04SZws=FMST2U*^OqhLZ$d>ai~NSH5#JmP&GN<-9?yjquLciM5?JRQ7d zDFi`Na^va4c>MZljIN53h-Wd4p3y!}X~2-2H@-6OeSGq+Xk5#N_*Q~4aKv@>>}ojL zp)61$|F-UN^;UNMn4v?5eyUI@T@az?F4V??1wxJe`}^n1aM9^18B;x>G_tE8yz)w= z;ny?!F`+Dg()opN@`0^1r;jY>*;`KGvf~GkXmMKw=cnFw4TOMd9W0vIYjgUf$@4fje4SYpw9R-|xrF21eebL+VHChYlsQ+@S4#W{V1^ag| zVQYSHas@N0LpYsCW^g-oI!ZI3IKYF~9M5+?I3CAVq_Pg?VN$`R5U7m9DZ@*tj9PBv z{=7SrzzcP6V#(gMnq*Ic(uz{6NItgdIrU0v^$6gTUKlGc!I?Lq=PuL+3l^vef7^VP z3`I^;saU5ZllN3e0gV~O*N-dXgTtfP%3V9FvtdYq^0a`jpEHt2ZMsl;CJEx=@3ZUMpT(Y0es3$qTnm=cA_#A(_bH zIQg~8?JU?z69}8k8dZTHKaIq1T=07RYUW$3W(ZIZX zrF4G7bfGp7LQi;b%PBGxI#VTMs<)g^nBf$ec`Ikgn*x}{tLhty(E)EO6j}^ zJ=guARmQ$aS|O>A&|Wfzfaa{`xbgsZo>J)&xj-^&^2@3^;5kU)t%jlb%qfF;>9$k3 zaB?-tcsAFLsH-4_fULvx5v2vR8ge_EI$H61{c5jG?-4mXx)n+*O3h;9p$$vbJLyfw z0cUwq`kk2}r!|K?OnJt^d>k zQoE~R2yCSp(=Wn9pF5FzKQf6D%g9OVuH&^})~IqIKby+8l{dt;vOczzpb_Y^pF0Us zNRm!U|7pu30H60ZAN1xPlF+keFOdLBI-fDZB}K|{oFi?uF@F^ZrQxnqt2n0ED%hj# zkKtI_;hA*}Na0G_j*M+n3a@!S+;I9ZUcBv8E}hb!WFkx2R-Nhz7kJfG5tEa1;i^OL z=S$<)8dlMiZNU^qpY_~HP}-r?Dv^JE_i?>7ReQeHdb|+gpp{sMAhZ@s=gfA7FWwMB z=})S3QVY@ji^O*|q=4qEV*c?JoZjCJPZi?PcD=#wFuw7xtC{_im-xX`8(ei=bW8=8 zLgXA~m-KVB7q$EfpoTF}h!rG5sRmw)~I#TKzj6K8HT+ z2@M3KDYnITIZrgdY64ezZ?%umvuc(Y0HJz$u8bCs)S0C24XuR=fhk;%_GiWv^NG<# z1(O-PLIx@L%D>*__s?%390~B1zrVwkzkQ4PL>9wSpF4D(Iccr=8;Da@X`K0v?kK;exF+rXg6gd?&a4?H$5iW|h|Nd(o)H>YXvpyn7jU zKeHJrT(Whv;h*mbca-LgQROWC{K@>{H5XT?4H(|Jb@%1E2D|-TNIx2PE{Y+3tHcj4S=187++}$5#1O#rAyoPB0DJc}gV% z!$!eQFrPFpZEWH*e_TPpa&Ki@32ZA(Bxs@p-29hS%=^uo>}XD7cq(m2q_uB5JDw~4 zn(0GW_N5v8_l0993K%5f8E@hHJi3f{@#kR`A;wony>0p5j5iwI@s@}^B8EqggVKr; zt4Kbw{u!|)vGXjg^^gNO#|}v7C9`n=?w!$Z?#wEKmTcFgJ{}Gj0-7?4uTLpwdUfGW zFu&W#riK(({CYV_TVWbGx>S*<#iC`~nQ_M}JhG}TmnhO?lon*yLLm zjbYgrr*qw`;Yc76&$^#G2(exJS#jLJVnUYmQaKL|zW`%J<66(V-Df{{1vFquOKgqr zbe5&xi2#>+Z{R^P{Boi*N_os^c`PuX+kMY8P%mZ&0P&cK+M)mEDP|h6Wrp1LN3P-Pe~!T_RlL> zw!VRA#LQdD+Da3$3_<~epFF;v)9!qk=hip6(wdHix1d!GS01rX7*fKYK5-n+e&G}@ zKVcwfO(LGnsp3Adl?GuNoII@5Jx(6q<%j9k#kR03)qo}XDy+H^N-K)YsC;(IYhq2y zrWso65&KiS4>X<~@8@OS!xf($U&2XML0U4p3$v8E4lZFJU^^UfxDDT6j}GKT#xF6yS|5&7679t9<=G@43=iblkQE*B4-TUUf%lP8wFq->#a< zb6+@xkDM|XsWpjs23zY~if3t&#f1J*Z~uI5ihGi8H?1e(r26Wgy8)&&MQyU)c`LIn z!~wkB`#c3--PT$U2TlTTlr{oVF#q^+OxgJzFuMcNvzz~WN*TwN2MTtA`Rh&n?X8_G zcxWx*XrM#(udOtZkclA$-+g#3Gw*ttXVx`znr#g(F@0hy?GpR{eeyV7x@|hw%^E>T zz#tyal1FBy@s;>612E z6yUYZ&75`5GH(6H8XCPs5ub=1g&)86k2hs-F+ZC(k!82fpyuMS3@Q#Vre7qNKb{Z# zHN4ieY{cwqXwWUtS`jtE#tU1Qi;c0Zr)#bId1`lVH1Cg=+GGAXxO$Zaa?7oLeqt%d zR|bh?b+7PxO92T-GqTv^e@^u5<^n3H{OPYH&~gqE)Qe)I>C#zZ0wwD37$!q5$vJC7AoGj zzBGQX#Z70GM_vJ5kA>EnkPOOqcCTl3^TtWQF`g4!!oGy|O~#qt&wU=VzrmD(?@p_r z*b?-#gcE_=BtL2j{`bU+Toe1e?Pl+K8j@K)^~aTDyuM!DZ6-QOV+cVs8sME>aX$K+ zH#zsdWjwdOVGsMdyGYjEHt$Jj z&wf@~>mp!^w|W01TAu1Xw$$QlQ_5-1svZ@aHUudChcxqe%8=gBxMiO=saJ}G$kVjfCj2;4w5JE3+TmznCYza$%HiY%!&q`}O6?h|`!}@8HH@;BE zgPY@21SIxh_KmoC+L}3oLi}!KHHyMgJN@L5U%a@L&;D@*k*Iap$hkcad_^7(x=gEkBtACqUcE8Q4=Jjk(){=D6NCW|C5il$aVR-7fPx8)4 z-8ph$AZf@n>))U89dSXw8MA~C&uFcUwuBa*x9oY1o*@tpd3N{oiF5a=CcgV-Bb6bE z-OY_F{F2DC28H?U8Px?>QTeHz^B-Nuf`7eBw7BOizJ4h9p6|4+u+ujE%EDZ8>R_%r zZ3x5LsN#5^>uR`?+pWgh+ey}xjqzT~W|lXs;f;nhtZClBmc&k)vN3FJV+aFFngnE9 zlILg+ebO9oeH6Qrb@umX-fmtu-~%@aA@0>$oBrJrLZJ0`DS!UzH?3!V?0vSU z>S)f!$tbt%(-J18bjdEg;|K(;7WvU7)eRwO$h6oWop`Z%*ZA9hD}?w=E1_#@q*$;( zMSnMD!l^^gTJ`TU?j&GXS|CKek6n3{OLZxS4?MYp9Z9F#>}f-|t6hBS#EODzoZVKt znzW;te&@@)wWWn{=9I|DglI|@7HP!zTJz;V@FH)9B=k1Wr9&QaY*Y#)0Pd|wF; zU~8MLiCQ)#w)0-|X5Nc!c1fPBV^^x4n4Ki6vN&2{2#FLDQyQ4UaHYBsxc+sKBYWF_ zeV<5ABESC>-1)9;n6>r!_*#m8{>|B#Gj-5Jpe3t0xnGb6=JZEW z*lHKPs{NIJzsCPeL9Ze=>V_1?i$5wL9^ub&*xtg$b_+!Qzas=1t zob=rvl1`dksd~1j>ev+D#`}p~Y)R~7YhovL=|&ndEi`3Y$S50I+t|u!CAV;oYv4`* zQnX5Wfyfa_(3V2(`L$;Z`}?%L61|^^#lLPhQAnXsx!?Wm{!dd#Ole{WgRl`GY=rdS z;%f2B6Ti80P`KY%Aw=^YPOoe>J-Do-g#UZ@&-%haGZ|ex6s1+($pVHKpFX)N$Ty~z z^R;F5R0JhmWs3SW&Or&^oLY`4T2=pi;Aexqw5f?Zp590((z8NT9fcpmmI9(tH`DEj z)pb0vx|T_UiuuIJgSh^*p_GKoeFk55LEy)=_asqUlDm>z5H^Agj}BybbRefz9=peJ zGAc`BriG?#j9saEcBSj7OECZFP!70+URqnN0hG{_16tBx$He@VKjV+nzfZ_U9*-a%@GQV6QenDyz7D;VNuLVOc%ftKDAs#0H3j zO$6}vjyPZa#~Lo0*pJBri_lsZ)EKZUu|JZ7ude5Q^9l`I(gmbNb*O^sPz7U(haK?P zS!I)S(xjX;NheLhPSRp0NZ2XjPLi~fCGI51I9W2vro~QR2#Kw2nliC{&S_t0N~|cB zbXyt+jQ~Za>zFLDiZFzv$czv&0z}OS5hF;%2op3cLPmg)5hN%Bh1@ECS?969CRakw zou~nXW`v_0O08lZ+x{Yd-t-tB9)4cVR>>EBzW0I?%6YFj%d)yOC6?$^N$rS4NQ~3sM;!uWRSZ?J>Up#Cn2Z z3+E^xvk!qFYip{D0~7}he`DV8TOju&__nYVt}1JYRwtQnGq-xh_p)0(b=!S8*lk|U z9xs*WYtQ}mJ(j25=U#e`^8&wiecx*__Ok!m6vp%ikkq)3@|b_U-cM^iT4xnnYY~tZ zyV4EJU-2`bwW)f(@bkTgL{08KwGx3Vp*w@qe$4rTAra0S5^2}+HCn?h|9FQ)+QBeH zj~k#}2_XPSF`zh*ThXJC+vhikaJ2jCP4}*i8!ShfR^qgh$J~QNtz^pYwda2O9!s_% z=3dUZ_A_J(fP@+VwBdw+kT2>S?NDYF^Y{0k<}aHc_tr4lr<(GhFoaw6bV_xI|D9G& ztP_ej{7R}~OY-I8%5vNCgjaV3l{DXfypwX+4au?%1T&wE(G!msB@fB?{%q2(T-3*7(qS9WvOBis4< zvU(odnq+s{aXXEQ{YdYl@Ae)p6az?>xa&U)twzhRDL^~;&m2uaTGXW*`110*aO zpOLSAf9cpV<{lGmH>ri^2pdykaqYO`f*oP5Cl}^Fx{mF22?C}(st24eq4R-%n=(q% zlGW@;+WcW{3)enZ%Y~2c;Iq%w^82+h)-+{vCXgZU95;Og??U(t7@@T;GG_rx0j*>O zra)_<+eR;Z)%)=sFY?p1i@0&jl@ywAB5;YPfghe!$&Q4>i@Q>k1q%yjNg(ZLZkt?6 zvE|0M^Yy*^j<8oYHSvq*w-Ab0M@#}qp->vIB>hX=@JL>BqGyUVrWLWQB5X(k(v6uk zq!o|6pWvbQ6O>w#Q6(1Bs)9_f4sl#XfB|7+A0LPBgLFewJ*z=RFN1&U%792Nu|#rU zhT0yff3WIzoLYGtCss@-DB;BaR;3|m2=1Fv#fKN~Vr_GVA}hbd6X|7y%^VQslHpNQ zfsQc$`@iz<_sH5B%MwSY?Ti}|u}n$-Vvo>y+($o((418{Ux=TpZwe?4xMRW5u(Bb; z>vd^<^==DQLCLtX0LNDZIH4-Yr1Ag*_e1cuws#-N+YKW}Xe|jJOqZ5I;*jB%*MG>r zXWd1qS(M9$$rpZnx+-Mw%hRivw|E!3(+*+d&@pO_OW-d~E=97RW@iV1-?ZRQ%eJ#* zbzP5ZTF{+v?OV44N_COfSigX0*pyKu9Zke+&C~Z)Y9A3(5H;P%LEP57urtLo+mo15 zP!W`jEVh_j9$;!^fQe-RhD1%G`?0|^dp0lZBlk~&Fbb2eErS4 zxo_InakPUJM!_KuN5hC>i~CQj;uA}Ekx?2;9=MRh5N`L*D~^dWttwcMboQ6KX-?UE z^O3a}mOQ$9NDFWrD34gJG3Y$t-?80kb;!xL?Q=8)6j`l>5(!)LT3wo#YEuZH$dn9- z7>q8lm{4Xht~9{d5{rQmgJMhW_c^<%p?f;>e6st$A8GKfb?@s&adkt{}oy<2o1f&THP&5oQR8TCMR`N5lJZ zn{_Q&9^T@=FR2b042_zMEU_39A zn4g?d$>(3J^8)O9UZf%1REH{ZO#%vmAEUlxScIfg z$roR)qu7!;QVSrXU~tsr+Hu7Nt$G1&m`X~)cb-^JLvz|)E%GRq&OolNud2ubJPjs~ z=jLYOoZNwL1V|*Jo zz4m?n-}L`Q3Xv-^D+Iozo_9=?SXNQAxPmm^Fp4Jw#afxuQ)i#ec@wkXvyvwhi%R%FB+gp*e;=yw#HVP zlyVzuWE@av9OaH%zt6q&;`jyvqs{q8OhMGUekmaBXdN&OfZ!pU03HZ_eEO-KFH^Jf zXME?l8{1`1gDa_R4OfjVMgujk*Ha#lq#PJsV)2R5MQ*8FLE;$@@IQ~OCzZB|9>sCz zw(v5Bs4AGyE}Amxu&$G_Kcf3EgEvOcthkWyqaSq(Jjo{bPRwD*8YYl1RBOE$sPFVAO3a(BT^5pw52>mVCm zWEMQZ2;r(O-+gMs9?1rM0kbYl(Qu(Wb*D5`xceln+~SPxaEkRIZ)hO|(SXPRO|6U7 zL32lr>S%`|Gt7qAHm-hU0Wmv)@RmJoFA8b+2W_PDn%yj2vzt(#blwMAyCIrlUqTlk zqLa2F?ReqIzT4VkAcY_xL>7p(N@zv0qdovzyZy*s*}ak*Ui~ggJAU7zcAjWqpLbUa zaHVr+$4ZX`_#R)=5R?><_UXe7u@u(bqz_#Sr8Slz37NtHQaM5^3T#Jm0NC25B2dD^ z+n?pu*MG#`txUTLj;9Jfv8IltYj(S~^HH7^wqJM>I+`PNK1n4m(q2MCpBdA0;A&h@ zY)Sw}`l+8ns(Nfq+Z8F>3qPFor;U&B-*4TE^z7-bB(*P{zyH+6g3}p~8VEOWAsR3U z8iv0qZ)BDG+B-eB4~Gv5bl5V9nz9i{NM_G=W9mY#RY_ap)WCs{u5rSr4v8(8vQJ)vH{lucJcYY3VWv(T=JkV`r2n=E?<1TLYALP3fxxzP|J z*4k4{=tiwQqqmdbXosp`DL-8OJAU-e?=gfdxY1xf@K5DB-(r|C;;WTZkbH>`o-L z;}wN0-`c|CtLg~$dA1J;LyE$Bv3WmZ+0HM_=tKJ<1lXV}LIy>qhyh3zxX)|Dr6<%1 zA=U~@(%G%tl-5)POIWb-ety38@0jgq349<3FVp9L9$QB`?O+*wX%7d60Oi@xo`f!h zocS#-+=uRjt)MDo5;WzT;P%+l?y4CJ6q-iiHd^jfLi=kH6`N7MvEp9tT=S<~pYIMk z%DfSI!jKY>g0$^mbmkHrM>9+FiiZA!q?x_ft^8Ho6<$qWGD%Ux*BVQ zv{sY{iuuv%-}7H@-Rn8YIyQ%x(rwFo(y$Vq`@+ebKe?KCO9n%ij6Rp)kpWvN zih>pcqE;QawHOj$=@K^%lQj5KpL?_Yihl| zpj|wX3(`)O&yM~O0dH~a&L}81ymr6>_~B(^`O{~Pqc~uYNZEZVIDjTs3tg!D!tN98 zJqAKR#z7B`2B;3nbpW&H&zFAeLZh|VeDOmK2;P%{&JSJfaWV)}PMTw)gScSeX#fSS z!f_}_uLjdrnvb3^kmqhWg%gLBx+-{qG1wy!`7{OGADa8T+&u;Y$STdq(jZ||E(4fN zTN`@GY{UEDn;2c^7z3BQ2`9xRgJ)4)Ifv&a9`~}UU0fT$zPT-H9 zo&}OPH_fgpCliyCdkqU)eq%_4#Q`^BiVE^hxW3p-<)R84y6m z(PBc0fhlx|9(~&veO-729l(m;-W#P6EQ+K+9}8x<~G|G2V96O zU-%8qZKatpqKs$0a57&yZxptzNTgk9eI(PmFVUuVLxZ+VX^b+H4)C(KS;1a}Uh1uL zB~Z_(l#Os3^LH9-?NAaZ;_{(q<9<(JBTE2pZ;$8N`gVr2cGpUl{NS>&JaOYRP8?c7 zyyeKYty9^);8Sl#R-yWrM43<)cpKQ@HvSa*5}LV-6hJ&=FIO40RajDVmK`mHByJ}; zxpFe&iidmsyz_I0HmR)J$e^PuVi;{4X0u0E@az{(;Tso^K>~@S zjUkV))KLK~DYtaB(Ca#qZbNT{6p*x)9$!_YCze^y2qB!si(3Wno{SP`7S1(xp7%st zNd24$8X7G+h0p?lqZJ<Yedi4tGa{weK)< zlq;?O=fcq}z4>J3jH@6KcjMOP5hShKJ3*BMjNAm+hr5t2{friqe057d?UeQ_`L?Kph3!3M8YO5 zX@`&h?oB@X=T$VP>|9u?BeeFVwenUTy#BPIEW3R=*UlV9##SV}@9DjRTaff@==S(9 z`f&IlfRuwXrm93AUlo2%2vJ+3wG=`eSVFU4fkGoTUi|k>C|;Ez13K8VTca_B!KH&| zdw=H--|_YLH+Ry~k|E%=7;==xGz5{b#V?-O%;|T&%=7CT-Rv*jafg`{?r?XMW>9I6 zpMQ7~Pu+MDr;jQp5zmscmHS=2Xq0PrNkvk&N9a7y-Tzo=K=v(za~L57B(q9Ps}74n z5#!%zE#~jV;r;5pvzE*<05tla9nI_Z3o%9)4`W8vao&mLuWlROaypCN+y$oCqsxZY zF5#j@7Av;LIrE-Z`SxS$QQiW2ow0CGqc^N&wvWOE_gi>79Q*eK<6vfOytn#(=08QEdH91M}za%ija*zMk5R5Q|*3yFDl2 zO6Q5CBbiz@7LCr^yX{N7w5|Bp%36fAf9bsUKDN?C!X_C<^X0$2!`b(~#`3K#IkmVm zdUm%}!$(&|`1zF+dHxHh@Szh1lFcd-$*i|#cF!998jw;DOFN{qPV2@)z`u&HAqg6? zU6Vl{ItEwWwv3ab=>+gEZ}UE}--8f(sR9s=@edWZu953L_|Gj`>BmLbL_%uz=?q;Oj(&lwOB{ldoY(Aw+Cv!Bl%^hisVNI*!be~Z#W zXr1@`?5o=oN{(T2+34K1eBi6w@r=Vmt7?&!(e9;T{e|qJMFC#h)WkXWF5`wptJ&$b zBk~hTItnQSQn-#Yt>Nskl{|X=i9B)RiF{yce==D`BH=pE4p+EK<1L?}HOY)>v-RNc zPc>)=EJ>eJtefFzttyJb#%U$?`y9Z#?c?AKo3qpbu#2i5cM|q`5is)3?RM4eENAsU zx!|R5lt-}V*Eh0uXPlsAbVx<^ZR>E*Bw$H?x@05Me)J;0c=3IpHSM!AY(u|ou&>si zJ+_j6Tys22Z$60+pEL*w#N%0PFWhxFv~k;k=9FFVeujXEDf{}0bt9yJmaI~z53OKE z|Ihp4xUW#SWuuCgv`grGuhsupktw^dePkc@h5)UR##!ZA8~8QA?AZsN zQxB;FFZG;bz;9GM8xsb`q1uc{;U<%eDIS(nw?%>KcFJ~m>fPN4tMk{Dwv{Ge3L;U9 zXWpyl%zIwt>IdFtV|}ty?Q3XV>Cy3Q>?wnbxo_?Smfb#`1(%LtbX9~zJWC>Nx7yiV z$Za76C{1&^uqdSe2#USu(Wi2EJp_=*Ds|kT3NfdD@KGtmI|p)x`8YJo7NA%-*I0k) ze>P+52W7+(hjNYyBq^0)X20VJNUNY^5?|eZX=4*>cO?i}T@xnp!%mT~iDgQD{oEE# z`q4{#`|aMO>=UM_d zn&zY@q4V6&|6H-vS8df@kOC5p7MBbOQXMw#_U`*Z2Sp)42w|A|9-VQN5C?aTX*2<8 zaaRA6{ay3^5`VD#)AMe3_2TWmiXDwwG^T96@!%S!{pclr{nAz(ty^QxounoBVJzi& z;fsSNpFU+U&wTzQp8LW{eD;i?R7EUT9rvVmr#sQKrl}n^G(brK;ln<(A8uwC&hW|- zd3L3G3wTPq-tB{$y!jD&-XaHGBi_6C;g@wp#t3$S z{d_R^vForUi9{@3-q^_HzkGwU?_I`I@7Cvtt+ehmV*BNqeoG}sX$F-9xoPGwUby9C zp8LWneBrDS3@;CpNMuPQvbnXo+8x&xg5B{T+b2R@J3Zd0s_! zEwE5)EoSGD2a-Rb=Ph!mL2kI{p{G>Re*VBx+La))s@<-hg=A3s9C>7Q9Zvf2E!^&B ze}#i4k)X*_@9gI6dzW$X{jan5y?R$|?qrD#t?IZxu>hPpqLe!?AI}?Kn$BOYp32;5 z11Sxgt|Kj(&6x%HlG|$Ns3Y0P3E0q)lJbqvLJ@(FS=mFsoUpt6|O|;fL zw4#=WSJ!drTdpNU4S76{0Q0dNFUPlNpJ-ylwwUvcU<{dwPdB+c6OJj=1R@d>@ zH+S;l#zvZ3vLFR!K<4Vj{rwJv-LXu&&aFHs5q)ilx)iq3s;VR^FRjdOgy8Qz+c~eW zL}8QdyhRQ*HFDFX|9Mm;vd@XVq_hx%lu9$HY&3(y{k%qi`FAD}0^X}l@aEPQ0)h5t zhjljmSaKv{60#)!T)vYz_blV$pSu#fZH1L@4|f*YN^SQ%G(*cmTr+(LkACh%UcY?? z_kLs&7f$X+Nzjwri7Xl0z4!Zg%QYYcO)0yelFznH4lLQHa(5LB0gY)#UpcNyoLCk7 zz7Qh5WXY1eq%#yMp#dhXay@NQ%oj>O+4ftcC!uHdn+mweC8o%)lv;UZj!(T?Pcoh% zS`_G%1_hl7KZF&DSf0e*#Y3y=IA?4npFd+57f-6%gV=um@6Ld4zk@sZDXlStU{qC@ z8%`g}4W|!fbA#Ka>EYFNJioqytqm!+R?;*GS~BM?mr~N0%#z9~!Ua0HiY-aR6l7GN zW2`HXQCbZuFP4}0v)_Zj?`pJ`vkR!9D4fu)$6Kx!3!WPJ;6+c!BI_I#&pHTU_}O98 zE2jX3Wrz8K;?dQ0AbYk-mm_xALTSwttLu1Tbsf`3mT}!_L%CvVf1&{+_gNtdUm@gR zkZq3V7KONf4=WGzsguKe>f}Mx#k0J;sgcLt+0C=-8d$X>PCTCRccw9Elgc`TeQ|q; z)ALWi#1ceJNiwal`kZ5(3RA%Dq@!<|Rw*Zyi*E@bl8d!Ag%Ei=JleM)o#*iuCSTJT zH3un#&_btGMhuM%U{cvApnzfwZ$Q?@vb?;h86#li5=?qBT8V9fh8H(9^5VJ%zV~D? zSD!kVPn1^H)uxr5Tyf8A_`P~w z-Q2|1rZjc&Eaedky|**uAaEtLL?s<}1or&L)GHv|Bob%BfO6x5RoP__ctE?wPz9y* zwSR0aoNF*|ku&n27yn8YTUQZt>?XU#oHz7b?wR&g9If(hX5bepJh);PAN=|2M8a05 zWoUILd=)#9aUknZT@mKWsROv`)WJ+0T%7xyE490}FvEA$!M=8KQ_^Pn))poWETSrE z6&zQ-;_00%t4$NNL|>9gC&3U_i#f(436tHjz1|M9+3`hRp-$Wkk+PO6*)6d11Zvcs0HZ9tfZ zZZ*`?;iq+4h9DZXP{6<6+{xv?c!QIE^b+5DaswOdlQ|EzfE?Mn0s{8%c>5mnLch(= zIV`dy^=XHUqfvNG9@@P6h@KzZ=K?}&ZCIB0+(;uHvgDU=7q@8FVCdL=*9fQ)=RfkY zwLE3NI;nc}v5%kjQ%4rC$er)qesfz3C;#{*w;JdOgTS?&(Y`vaC@l$a{)8$%HfszvHjM;2YI@Bf|;14!hHe=8PicXi|mATvCfJX;OMDexw7%Eob6pKzo6v23SK2i* zeCevZ_tAcF5rm{5UN30czs$?mvX2}JJh8V zO<6@Ot7!3lCTvYsxzf(o8e985hdsZyL!)`G>o7d0qA6Uh!t#EG3|F!iS%M-{QWB6< z1SM5LgQ}20Wl&P-{VWR@L@hz!z&FGOOj~iEWr!PffidU(q0@$gKeK49F9!aN8$Ua! zly;Px|NP{;yE*rsWrTueuXl)b1UXw9$k+-y@LC}-H`Cf`|Gw-!g35ftUNSF%5 z>42);_WC*vZAkrd;99&H=s=(M?dSh`Xf6Ny(0e^H-ScpvZFX5It56vS48bw|BAhv< zf{Q2hW7ep0%G*e8zxqoGm*72w-}|7gin_n2T`2Jn7qs4gw}FE>#Qa@xcQt^sgb+`6 zRMp=5yE_HCwO?=)cnuhXd*b_5z5Ue?XW#uQ&%9SpBxD{H_H&_Vt;2?t?VvLbNWtKW zFlUS^=fa8oIAc^f!^%T@?AKFN^Y)_cNH|>1vHdy2_TJtO_=4_-LWnPShN|63=d~}| zNa@pnCEj<)y}!%1pSL!pnEbsLiKXpa2ai4+7;U?!q-=%qmb57^2{LVH3FnNfRdkwvR4(tZhjBHQ;~QkLvB)&;MMxix1rY zhAW+qV*9y0XsecIY=xawpcGcfWL&=}XN)fAoN<+$GQ5<5CBZ%RZ7u5P+`^GQ92CBl z5eH5cLaaJe7tcb`Wlz#qBed2J0~fHDlgziDZ~ey_?tF3s(WupDKX(8=x&5rObXK_? z^`)S)B*>J(#mpR8&gmn|IDT*ml~L=E$LvF=BMVr-r-Tr{?<~8yExMA>{;?}@)r(=Y zv8R2<$c#H*=A{jdgpYC$cW1*7g=yMIZ%2bA8CdEPeERTG&K+OHsUu4J8vk`Yd@c2- zLWmnW({65yE`5e=RPPzUQ@FE}R2m@#Y;H(#?Dt>rR*%Sk6^A|)hPKjMX-&q~IF5p( z&DarTto-T>EJGAbYVGaOHxV2XzIOToa3(IfJI!uxi!SZ==8@SHLc9Rnf_qW6Qm)DH z>gFbzV;Ofa^^v*D;1^@~$rzR?h=xo;WkKo_85$B^OWy*Yf$+$z(I~BQFReBHe|^Y> zr)q(Z2qDsRLcIAv=<)`Ro*iupA?^o$0$AFep?H2nBlIn+((TAqTWM%Y*=%l1x!VgQ zU~{IKjLISqNFj4CHwG?pvU_jBA4R11FD?yyNC>gn(^|W1y}$N#EGZn0*4hAW2PRsE zJRh)^ZEiL(`c^YM9B}iF1?hy%#`+}FhL+^K-iO}T?Qrto`hT;&X#<0z1E~yEP#LPE zB2-R!u$-bml!zH6Xav0MtwSGJd)X!jCb`&8if7MJ`E%#(X6|WRRoet^5<)!NRd#b* zbPJ({5L)ZpGPLu6r{kJwZ*6OKOiLQN&%1qCAON)Hy`LsSIn#oz z+_kR_VPFV@uo)s^Mu=JwN&+R621+On6uU30h~hvoAu~kC2oW+v1f=CsQ*?}?ZAsM~ z(%+w`+G-@;4;$BKG+zku%kCgEbf1LwNiBsCEn4f#|MS+a7h4>2a6n`nT|oVGA37U? zHMQ|}?O7eJ#xeqg%@9hvz0-Utn{hHE?Ig9STC{dmZjDCLs+wDdMaT#eG=c<-fI9)0 z5yECT_lj5%LPm(N>Aox(z>*f0v&^C|)1X|D%KEw_K(1X; zzBH%|RbU8-qnv%Dp%4N~So(3?A02k(5(DEy0%2=3UFFf`4<=g z9H)=e-3bk*A*hRI*xZn0(tx78TKf9$mj+8Hwu-2=>#&5?E}M0aee$$>eXn|(4SW3T zPE}i@F@!;Ls@a})%!%fQhh6cqf$;$$M6$EfI`hF{BhS(Wb8KoBo9R0*d|YRf^ALuO zGz6{Avp#e&G;Yy;D(U3Z?tJ}|8jUGTD#MjH+UfRQaC(3LX`QpzyXz&9G^LvCV}~AV zer&{tUymBmi-ZtOhl9*;7zqu4N9xQ^F8G(urmsYrHqw-Rr0zcWp6*pUV+HN!tD#k) zO4L#MxC~*?oNl&{8+M%eiIE?9BP_!g2qAWNb*%YdIQ;6ZPwG3)`-jdX=3*FWj6PC# zH{Rai61q@1+dop>`6ts0UrO!%JecXuVwI{(!ueN>10V@h)m$I+1vLl`us8tqd?o??D% z#FZ~ZjqrIrfy~fT2n{Z&KYPEG`SC@Mh-~IU5Y1RYqmR^G3ysDwT~GIJFQ+>nZu&NJ zWw?Tf89r*%ZYd-UsRsMZ33JSk5C8B}5hHS*5Tf3*nGgF|?Ky-7`1y5NY8IQRJI-Ay z)9G`y*4qh047Bc+p#0GYt-+Fl-LWidy#$kd%2;!r?m!8p!7|q|)?<<|J0D&oTsNhg z?Msfm*t}xk2mdLN7YiX`hsy!BKlCg@Lr(op-gVx~BHMI^cCv30DmGE7kJMcRLkgUX z!|I(bq4QOqH5w@-)!}~F+CCyzJps4$)KQL(+X?-VsaKj8_B-z`LmD3tLZo^UnV}~V z8eCG(T5P88xNL34u02y{l8cF!nXaThij1{xh33s|?elc^kMuucZD=VZscc$dAg(## zGjeA6jIRkHZuy4#hSXY%o%=2LW4Wz)n_&>JzpoA? zkdhVKTL8BSVIde?H0X%T2Qq{~EE9M7mGzU?O}QpErTEw@g%ICcs23XZncrnK%^ksE z$rQ^Qi{?4h)JPW0&t?#x3*7$XdSMvf14CeE9fasB>gWV0t&yhd>26Nhl!VN@a=Lw+ zxjI}$*a#gljxoP-sxjSUPaQVJoIB$3wdF?H$Al2CbZ4POd(iWz)ddUOQL=EMk@?Zt zKhRF*B8{je80~YGbrQ4&%arVFPP4YoEv3w-q}3PX6`=}B0wt~;-OC+gQb-)_sFrMt zx@gh`<~5_Pdc52yJFPd68F~Yu0T7yb^Bigxo0%V<_lQU)rekNGCtPZxz4jM<=nxDc z$fg}ucy@GtvevCyvZO_Iq#w5G-HtIsND|qkV_K&C^wdwvi>lB6o`eh9#F%?DnW5JZ z+QS0Zu1?>5;ktCwyK_j#?;sG77?$XZS9c743h1);n+w{{pGyZ94eGV?YTJ<1n1Q2> z8;-lKc3Rm99~44-O^~bO?-k>#XWP*G!=iZ(HCnPjWYch4;EPYbfHdwC!J>YoV>SX~ z&qYi6kPj4qDOvu$=NKz6V{Ay#;9e=6rI4iUv~qCd#gi{G&+0e(=>UOig%In#T4vSL zJlO|B?~>3y76=6h=4vDJ#aKAVSv^UFeJ4tX;yiK8iizxxy=E~uvO2`7?VPh zaWX1yC-hn4=a@H5y8ewR<;TtzLVU+jj_6G)_TK2tgm&SFwzL+RAD?xHax$lAC$pGv zv58^!N$Ub21*ojU>z*B*cgC1@i~I*f2lQyin0H>fIo)FSFRzxLKK7H&MYim6;#RIa8;SU(kpKLdAyX3GcNJsk`73 zSn%rRX79Ir_jC6gUOcQv_Stq<+?1rUDaXbZ7fd|YykXLHFP%^^b(Rp~YhHJeUg5<) z6pjo+`&cl?rbbK9BJ<-j|4-=TNn}$C2}C4T&@IR8Q^OAk?N(~=`uojf9Gz<#m@kGD z4aSsKkGjQDNV3XS&6yT8e(-qXnq#kOy14&^w-?LenVwWOycEtOLX+!#UhYF-fu}Cj zEH={%=Bxv*4BYy`P z+hV~So0=L)jh2}oo%zpnQ_IOZmG~;cs3Tlr0DboLUeLIul&Pf6>svfKIv-EBmtYdL zqV$jUC#&qk7C4kbf=0)(G20B7^1_Md8=oD2%_}qeopyl`;-f-{wOVWQNRrBJadb$i zxeE;z&2s=kUwmq~GK{YZrLH0n4v=oqXjETbxC>JX;_(bO%sGa8uNa?O$NNxFS|Nqx zKN}wA@y$;XwW8gc+bx6ujZWAJCtwB5lZH*>&L-e`>()nqCLQ@zr1fFUfI%kSM{K;7M5g4G%}u$kpoL&W$#7&h z-a;t^N@<;Nl8$K@=E=vLWS&qmb?3yg3I7M&CxmE(BU8QJAC3+Q-4-=9l2wyLPGaBw z^l2Ktf|QqF1|S^=tsMmNXv{bh0vrVqOY+uNW-zQgl-DA5en0PqbR$1{{SLC;s+3&? zDI`iOmCPn}Fc>r@51hnt#gprgubg^6aJLX*JGexqi`HW9i8$&8Yz{Rw66WWeXrV7H znW<5?qPdJfSdxk3bxARLY+$vEU`RnCk>#(~p1_BuROhXB`%bXC-@K0vO&bZC!A|vf z`^(|j%2p{mrK8~@WAeaBOevYXOcI=|gS+m@k?TZk1K*Cm&KUjyC3+i5Mmb`eG++Z933|F!H^4cxx`K`5X*p1 z2fnoAJ0g?3Ml1Dk5iTBtV?!qCulajKOKrEcujIvzjqPgfdrZlQJ(sF!2YhMlC`YC2 zlonDL!}<+1#utxca>=9@hZPU~6|hhU(E@uA`G~iZ4~8RlP_`R2TC!?U&c^1}M=OF> z@IzYjX(8kc%m8GQ;Mk69V(TN))>ayVrr^yl&tPO_xP5l?0T!`47^ILWrF2$jovfWT zN{dUy=&DhSw2p~Qs+{mpWvKF(0cky%^?F+zt*Z5b&?lkyMowbaNG8n}%oFu#_yzEb z(3c*cr81dMAmk+?R8)=DF1eNR$juRxtYR60SUk%so0>dAYviF4OuTq>d9aKDkpZl4 zUQb9Kx;2r&RjISuRv9NDLcx$6UNPJ}COn+6rDNV4UpnrOz#<{UCV!vBda*g1+0K!W zuGyJ5A_iZVLTkbNB?b%Tv<{rx7FUHV<9w~04}+dVAQC~@kWIOcG}q|s)sow{qdzxm z1V5cO0Y~Y)RPTJ^(Zch8E3~q;tujtl1_GAsU)rDH(P0d=25%czIrgE-VC4hAGrk(V zaN$B@?%cT??P~SG&?lh}1y_}G$vk(V8({s(oVpbJ5%^>9mM2DPmADv#4+z8z!o@+9 z0y_NKg0dH3U~$gK6->#s;#E#xx2$_0eUccxuby+}~aZODmMhsEl@$V}wFs zIjD4y92gwH(7=%Gqsm7;IVdvVLEvdWehfKjeAL^=`JmSfupR|pYBO8qRCFMC>!YIy zSm)qyAut0gRDqOWXI+B3O$}RD3h&|fUwi+&cKfMJ9$1u@+U?uX@obzQzIKO>*)gTH z)>)O6hGEE(q7wQS_otEy28Rc(9bGo+=>d@e4+GB%A?p3(EYu5)xy*&5*kj!u^hxOU zpsm!dsgW>SGJm#{c9Z_s0zZx3{AB;E%$|m$&k|bC1U;Ehv3tLrEZ7;qtNs!~cs%G) zB)2@Z`N>7eb``1NbU6ectMJqsI)MBLY6T_mbSyXjzneVw`g13`fc{1RRgDClH95S{k4n zw4FgH8?A8!+NHPaW|G={Ox^yhOfSLYLnjVo(PyUMD74{5qjZ}WfU&>xd#`D2)2_s> z6{AZ>J>Ngl{{`SxKZDChpCs;F^bxwNptWGpA_;S0-k#E?rbf&A?5B@Or1VKcn2{6D`YBRfL3UwP<96C{;okgIhEJtxTm*k>9*9}2Vy_JwcVumep_p__DZ8Q zT4%C~s^S3eeEl?084VmPNjn9+i>skL4=e{(N+EacQASyVTs(iVtff}ZWo{pdI~je1 z?j8tgw16d&Rkb=7Ct(pn4cMU>o$#+Ckpwr0FfE7L@p7M5uc2^d5J zCLv4m&}XMGwgi+SDJ5Hgjlgoo;WU(=O_H0d`6z+2L5&G~zP*WpZ z^&Iw5(LGqepO)2^{%qx@qOrli$kp|!;kz1>#Y4&h6XUiRyt6qK5a^2XaA26NbtIW} zL?YuD2rNqqD`jICrip2q2q6iWf{+23tc`_(A;<#=5t{z?<+H_36Xsjq;tOj-g4MK>_{w~bR*b=x?L?3~>8GVHADbQLlA6JS4 zteUM^G+)Dl{bpqdT*EaSkTj>A5+JxM9yeaylnEA@W+WO`#dR&Il1LyBHBB86T3AIP zOO*zVOv+L5VpGOSqDD(J5NH9CKn6F&7ArDED&@F2t_MC{-PUf-+0=amKD_w zcbrxAng8F<_nh0O4?{=50s>YZyj}xT2k!}x0Xl#-pc8O_R$z1R-gcl%M0C01 zH8nL^jg5^07{K(j-LVQe(kE~@V2qJwnpMNn2AMl&u7_auc|QV-0Zz<$xhbu=){{D% z&hZ&1GqJ0XuFmP)@T`-IYRgl`0!KTz+C>|Uh=71GphSo$8%u0rR*a;bAYvleo~0|J zl};*>+*K8+>PSW_HpH#yCSW!23a}Q~A|m;H&l|At$*DzTuk$|EaLkf1lzz=Lvp^OU zC7&Q@qlW{hZR*@KYe#z5*=_l@Y3)wOad|r*&6q5mPA8c{hD&7OHpU2A zkRETG!C*}I7hsICEL$vPk&GosMw8SfYpF}tF+4e(TDyjnmvSp2$;~yNUnBfHT*1uA8^9WAnTnT|3U~q^qWjG@bcQ z(%CeHLIJI{R-&{e7D|+eh=>tUqJVuFXeh^qvNSbdFh(1VHrjZ`Gp=z1&KmLXtg}W< z=e?8PO$0mqR0r@PuoQR*cv?idO0MacBjzzd#)W2~Vkv6z+{JGAnlYw&YktSTs(G;nLXwr z6R{!&UnQ&HrxaW8iTMX$iHPhjxmFvWlt;CVc~q0Jsj10orkT(IF~&sJcWs)pa`#Jb z-d;e#S;vd2HT`LFNV z*17$i?Ow;!w8@Z8cVS%PS;{hkC_&VbAYTYU5D_wNmZY8F^6{54d(1@uXrl>Cf!+|3 z6MS-pSPL`(cZtZ$#f^P6epIROql%0H2|WM{i-$HpG-qwsh8y=}Tj%E^`BXa7g`0P^ zr7W$CScn`E>9;SGDBM6Ao>D)RYmT4Kh~#hpj4{4PeqacRDRisz0&p+zBN6#aal@mC zlt%>_8=D%fP!&g%%^j=$_nIBq*FVwarso#KA)C%3MqDMToUL`lP$~^8A}OvI|0XUN zeIC$nY1kiVjn)cf@1Ttr_N?O4`dLo^w*gHe;($-ep{~A%G#M9~g^ES6c=2MbP~7su z_ZDCZaKnOV>U^69*>#L@_gA$BcF6rT3TdcHKg_JpfxNHi3kqn?99KC}VVk1H}5 zv@v+bGuQm>Gy0L8E98m^7pnIjKc7o$XTA_qkz0WO6%jWGcgaw7bcWOe3=xKCLY4fj z=Wf0{?Pk7WY;(Tjx;UAFR!W72{g8taMb^!duoGM{{!Lso<^rJq($LceD_~-};E{^< ze3_o{u*Bl#SAW63ulxxk;&rqZI&?uh=KRsqt&g6(fH&37dPbqXE+W5%p{u%w92ra1 z$-*bUc7{>G#V#U@bJIcLq-*sLWx2fO~%b|a_wZUJK-8erbZr+Gz5bZVPk8K z?>(}S&(E8{h{_lk*w?fS$^C5WtIU7oQ^ZshgF%TxfG#Jab2@KkkDY0Ka`L;FQ#1Wx z;9o@KG4Mk|BI53Q9!Cmx-ja$05e&fd*es+AOBZC<6kfEuLA&_#Ao7+pFy4Ka0rEU}#9mBTB|Fe=H)NF=iBSGw@D;2R1(B{&B;ft;%SHc;<+IY0E;1 zDi%##F#3GvPkakeE81^q=xT!rY`=`E#x1IRbeiKm}&;b!&Ysqr$ zPoCu;fAJzMITxjbYtI+BJY_a=ehHTgSfWHJQ7ky= zI$~A~&v<>RyTb(u{_AqOIJtH*pE~2?oIdO{w9!T9p*MKipahf>e){BAX5IWazkBv| zk`)m|gjmF4(d8!;3Af+pjm8qi1Ft{MpSJytnrJ0n59+xmxW*%4$D}@5WBuTT|MM||WH#MsN`&UH;LH(6`F4ETihvk98Op$ZNE5y=r; zef*VV-R!|+DFz&VCdH+o3%q5*6@2*A4^kJeElR`QzA(mgJ1JY*axD1SbNu6-&$B1% zl1N6tKt5gI-Lu9pWkdy9?{6!HHfYW(@Qs&$Mnv^bB9?<6o74@lU(EDf$IQ>7AyNzNN}4l{y`4lVifsv68ywG2 zSsCTamrTTz3F81oXXY2L{fXz>R#Ramik|bq!qpxLJ1#ZRD(jyg|4;Y&r@u07kK6u; zG3LDiAyr`84>}Id(_Oew0W>cV@6_LI{lec{)jfBv&(r~)F+GQuLi55^3I<=%Z@IGWGaHE z{X$$z2~O7GeY3_gp)OI@gEoQ3y{FL5?Q5E-u#*gwV;9s}GCDcjzJJrB-o=0YQ1XS` zx;u<9Ul$P_*pY+ImBA*N@y4|rX3Bw!vLlBHH zq#`MRia&C@GkGq%;pYlO!Vm1AAQVasvO1STp-ORfAaqf6*Gl^t` zs4ciU41|N@8LBE{d}hx0UZtWS{Lk9ouqpjI@gTrGkZ`n1eYD2flWjF`y6+?IpV}Y) zk};-v!-fsEuXX8%T6P0_FYjr-aL+2P`0uA# zw_N95EwSiw(ljI*xaqWyFn#!${Zq@vCyuRz=eKn-`_|=r z`~G#rqZaXqh3oBqk&ZSbk`X>Vd%W*KmtD2sOT{0q{WUvtdx)yYpjM-Y*E%9G%>MGD z-*vyT_JKL89ZW(F&JzuYc>JBu4L8H;9#HY*`EEyxBch7-p{t=brnv2u4j$c+ z!xAX-tcnQj$HtScKZsvkp zp5)2b+DKML{kUQo^TJkwlXbb~^bt%Ro+|4>hf;BCb|=5u^Z=E1#h^*WQWy-L@fedD zVc+`9o$g0o`o<~1!^W872aAvcPex;uPsFvuqm{!SkXZ68ue041k;uNA3SbL-Vfh~R zBqK>&?GBhj6Oujcq`7$PTt0Bh4WuH;ei5(iRra8-(J6(aUB;w_+yD2O*pznp;!}H&;I$qJ zDSrHMc(Q?iIQd_L zF8Tpu%y9!v$blo{LZeuOncB3YqL8%zEb-)YH{CN4L%0TNql!Oo&T!ky?eykdL%01n zrFJsco^TCmH*-kRF|4+Us0bgJ_I@rHbzX6Ces3f%Rm zg>?|sW#Mwsx_BKQJMF_v3F3$SRm;0G4sZMEb9~{~ub?a;9xd;r?0^1{%X>^7Q^}jB zHu%f`dXS14H^(nG-bc!cV>q$}BaU_$m29wnv-$z|6R+Gn75I}eCJC5<2#gO18D}qD zYO!E}_p4Jk{f}KybD7)G=7{Pa5urQrAGP4qPwru3m)ma%Isf=8nK^O>olfTg5z_aV zyBJ@295yR@whfK!VL9Tgn z(}sVxs_JiWJ6jzQ_H$PHf;JFSLQ7urvB!6lc1;l1>lq?`Q2bq!-p#R=6zG%&4jAs$heta38_TU z<#ch*$XR?~+WU!HF^uWmQ3)$;JY)D+^UHkbu9xtDM67pjxg0DdxH*qEPin(w^W)_43dNEs&V1Rt1o10$0odd$*68jxP8fMBvKKi4{y0AXagwWJ!g*&8bj^BP59E?n;szN z6^3LzC-`=4ja6ZPaM>bnWoG?t#+Y*gLLQWaJh-bgMjD%&W&F=J#8uq>r9`5Y+Hnlh zpT(7Z!!R0Z=+s`Fnf<$%^6;% z(;eu=QpP}4MDgXPTX=YTwogKa4ozLWjt`&uL26?)K^3Z~$3-RUEGbzA`M-~Lz-&KdZEh=GJJ1S-_? zJ70AduKUph;Kz$)vF0Egngb_eld+a9p6gEe^`>j=ikc6*ovrSHc+UMrpv{OVxaqOo zJi8~4-OrFueL%=ziD6{i40$ilbtk@+i^t9FpQ94g9xlFb9slsN=WsE8*ybSAaw%MG zh{SE)e&$G^yg{RaNAmkEORy#E6FRivYL^j-di$p@{Koy|?gy_8?MDufUk64;V}zB9 z%<;e3KHMnvL*o>*FnD!t+CWqZd2RT>qq|wt;q*HOIXuz8M^C+xhC~C`oG_o+V=nBI zh@ms#dWQG^-%Bj|!z$tl8>RdL*h7I5p-}KRYkW1Qj;i!CF8k%7eTluga}!UuyiCeY z9FdI6!NAj+Vewk^Ps?xhUhG`^U1Q9NLD^Zq`Efu*YHUWac(IOnx$j%a%Hg_@GcdT; zwHKreTk;+^{B0Lo(r&*9X^f#ZUdtEG`V6zjT-Yy<*%F~O>+qHzuHcSGH<7IHiyQ_b zyr%?kHP@ax90@%0eW3#>c<;8q(O&2ZB;(NmgTWSCrE_T(ulaGx(cX`gsGgScltsUZ z)YxRK<^|$SZrV8CN>wj#yV_k5by)6}XCP$@o4Op{`^Zjq<-C4Z`9y*gT7Nv|*IKg7 z{r*!txN;|{%IG1h<>I*zl!~PyTz%>YpnM-4ODwd}Jox$(#H?tMoJR{h<53%{wjNmb zH}{U6f12xQ?*j*tzwRFyjgjV+e%@Iub*ph5BZr|3y%b*H(5&fjdH1qzLiWZNzY%Tk z3+Y3T`T1>~%=zAOp5NR-vLbp&j>^7JN^tTXXN{|7QbV%XjJ_0M9{Q#Bb-dWNmZTLQ zvR-s=aJ9#-_K;)O;vH=@!TGk3>`kW z25Mr8m2CyywR8ttGj5+t7fRs;)ziOzZU>j&_7pATshoOgEodrJ7q23^1Ro)`{B8s(QoQ&g<-E`Yz%%IC%;%C zab*tYI?;YY8^hS7O=U#7Yti~Z(0Pt?C)Duw&z!^CXO1S*RVZ6+edy;0*ow<0*Fz7d z7Y4eQ?OYKoG#hfa`Jmt#O=Tp-PuBh3Y)Wrm9AspAyttmp7)ZsFnl{Z;vBaBorb7>< z{)m=9mt#1oD#}kTY@j}75ft(C3t|0Xb=>Ce_nyuz3#NfFWb-bzHBjqnAn$6%)FqgC zTs5%2iVhd&w-!2hzHN1&qK^$S8VpG*ZoRT|t^VDfM`i(6^d=p9Bx5KQmFO?2Xk5&Z zE;BBH63BUm`k3N>E*M5lRH4m*_xDrct81+dpPDnCr5~TkDWfXMbQVw|W$O$Nc>j*a ztmCSwirfA|{Qb(seBwRZx{BAcyNhwrV+EekR7FzUz41OareFVZ5Xdikf#u2QOGQ1U zdE+#(V^?|U4kI!&y4(ujX+Nm_U*|V4u`+^ZdTz$9jpj#XRsROpoVkNg)O-~Bw!5bCxzP%s!0R@{1Z$2xuQp1)tGhGg8vGySV&R026ib7Di1 zlSiZg_De~I*`aOO-K^@^NL#o-a4P%QJyq{#8 zz0@b3Xm5dCQC+Luf;SYJBiaHj1p_myd}loX3>FP>D-W1pVIciuLY z+PF<7T_|qT6Z{ab;rxj;KzWNjRLLt^Uco&E?Zr}Pqp6G})!(+R;E^3Gt~bU+=gM5K zv;cQ6rZqM-S%{c(f4|`j?8w{y%6-r6AXO2?^-R$YOjSmCWX&GV z{q7U|eEBvkf68>9&W)v{*!c6f+BiRa`!xRk>2rANnIp*;G})|!Dj}r^6kNlonmAKN zRQT6Hd3&+l>0)i?W@1+Ku=buj63|8yx8wSemS;^%zWp7*UT(}@WSj%u5@T#zam^Pd zM@ZPB2DpIbfM5&k$!l($T*dt3lD&G)VFk{2mu}?0m%QfJBkptZu4hOlB6Q?E-uLqt z_=h`RBZ6>m1iR4nidC4DbELi`bSwV6`_&h0-mRimHxjfl#I2ZK(ebKjDRf)~ zuypBCfxY95VZ7%u0IiXjC4B9yI^Op1jsaN*(qAZnw4?d<*~6GnY4<1XPy7&tE-PMR&59e%63 zW88Tug(a41OShXBJJ+5BoJ>$}71AsGeGZTWKh_u)tll)WigOxbbQDaF#25%DfsAXI zTpi(G&ZzBm0C;F8{%rX+e)7mhzc1n;cw4SEL;_oK+3FT%fA1+i()2Q$T7A_m!7=+f zf)Xj(ls)k)B;LBN&1}lNPDI&QqWT|t3Bx;ZLCN;T7b8me z=GpZmZNWW=>X?3?r15Qqug|EXG7`2X*&m^u_|o<+KHl^)u|#A*%C16NGMTWkE#beH zZDhuG9_Q;1uBRjC7E_7+C1qcED5VA0Y=VWPm#5gKD212ge3#E67sIb&AFU7s*=ym8_)Y@`8^dO=uXy4uDd_b zM)N}38XnoXf=6~e!^-yc?8&#GF~n4am=z7yPx~t!jWNfHeW^_Zb?a%LUv<^!i>8T) zCxN|aj4`zuy;h;>C{r^XlH=o#@8-7~)6_)=j*y{xKDj2s?=BumG_cjmJ>)Q+_}SmD z=DYW=BUN>1>bWn7`1WAB;DMt#^|&fNHG4eQ&lpuCr5iK=IM57!xn;ClGdo$)`U;Qj zex7GrSFyfp3+;tYG=_-SM6C!Bu}fADh9z=G7ey_UX>~j0%Txc+`OM_&CyB^w5)K|W z#>@nsq=({t6a3TeXs&+fbvAdoBy2h0Re9wg_{FkK^F}aZSgc1|G0d<$uxbx)y6tHa zLBZA(QvO^-1}Oi^lWLvqqAN`bxwNC#D_%EBzE^_e9oGavEdUklxA*ZEJY8 zbrmnQuVsDLR(9uG$$EJJY_W(an~2z0N+Ch6r<BE5>ro*tr0-6x!L)wT;!C8+o;318X}svoW)s z-T5{;oiqjQ1hZolmMCmxVTt8CNT|KKuZ8Dm_WQRKJw4t1`BD1U-uq@R6T>wgj_2Zd zj(It=fwYr71^A1-l%)VXwP)qD|5)==esJ>V{hGQm%U!|+^V92NEIhN8FFw^mT}<`5 zh`u){fsAK3xjMopPpR(Jdk)t*f8h_S+3rWKZ4BXx4<(AaXf<_p)tpr~rMOMb%d;!r%GT^oHm0|; zIlY6;*&S@p?q*lMmG(j>ooIBJmVFMN)UfRm;}cTds+HP1_`=y>~oHU86;EO zkJ&$0bg%?C*q`7#kx|RW4s6$`vWclE2`fg@j+3B1x(v6;wwmsEnkjiBwV@ttgT)WhX*M`4JJBZL^?R`xmLlN3r>r7jKuD!%m^n zPW9NQCX7FRb$T7EI~-Q*&QldRFbQc1v=ubhPDu4i#Bj=JXU^s0zj+1gNS6`qgK!jE z#1bkhqqMi@Xv;c4@8(M=)DM)aq2wxs3G6~sBCY(KA}s?x&Qd5a%d&)RLB#ADWwk&2}-#;>~+OEGO^MW5UI64fWAXAZnQ zODRf;XNiR!*odKuHG#*ijm9$?&vT0d%(Ks|?gSu1j5{T7J6z_bl1@G?rLBHmeNn$1y3JADlNV=sDOw zJ1MNlxoK(@W0H1{wqh9Eer9tA-(Rwkcrt=_Bx~CGfH7b!VRUs2=(zwG;HB0AThe}8 z7E8LX-wVh&P~W%E_VWuny2;c{TC14-@qhMiFKv4G>E3_$zDxIS@3sHZV}zEdl-{CB zpiTiqB2K+1iH5^YBFg4V&)rIEp@XokTsc_c_q{oLSd4F;T~C*zdyEf;P~tBkIHNAg zdrqqCk%+|$H}L7-ucF{;lTZ&$ z=%8d-1`OgC@75+jJ5$pf0^N(#qC{xTIQ;9qYq6u&kgsY9zhpH@o9aZQ z`^6H3wsAolTJj!I<+q47w*RGkskOk0-Fg1!)eais7AICmIHNAgne{QI)NwtB>!sL3`1|KC6!1dCff= z)BEMHJp&0__{=HQy`E@jD}Ljzud#V&`iR984-Ebds82?TT6+h6_eGau=yXic?hdak zX$z^KtUm1;p4^$|(bxUK@YPYpxRlML>L^odB22E1FtIYi$b?0#mk5KQ+d10TQIyi( zhsxT0z7YHV9qvBt_7;9&N@!OVNJLfb2au$=xYutOZjpAg+_d5w+;h=?lCly#hI>jc z=s#!G(_YXldp%2C%#T4@;@2s@e^Mn=YNEY5ElT{#?b{#OL@e3!5`v)w@r_0rDtxIJ zUPmd4VJb(Hb#<9Ojop7+0u_;NXTmkGs@>tm)&kAOucWGs2qO{}<5CeORN73ausN>6 zW=ztiKBlONS=iEZJ?Mbq0L%q#{Cm)n*KF=`DA*A$s<&$cGMXw0oP*LGdPY-WCwZZ5 z4PSchR(>$!%e~r;$u^)dtVZOPtryAt)nYb zAeA~Aq+<6v>9C--w`@|Eqse)OXfF;~sK!Dle65lUo{+&O=xbdrYdRd+6zAMcI7v8Isdd@gERUO)lJx)P& zjYoa7n!7jN&y=c(+%)MOz1okVx~_~UetKa8?|EbwD_aY=#_++()r?MBeP&vgZ)~S& z#dhM!$WcMWA`C^z*u&d^Z#TOdVpjh{IQx9OY2zPbPtId?yHi}J6MoSZ zekD>w`4ZMvf+haGB-p2t^5+&b%D~lrM>WR-*OmWj)RNhz9MmkKHUNHNf zK*Eag@fF|Xw-?;ZX*DO&TmCvEWJBEICl@yG{GI}JF~5&uPoW%334dMP!u>0Ek%%81 zw&Gq8p*Gof=fFe^E}7_JmNd`c0Pl;fD9J-_rPSUnm~Jm)z|>FN|*Y4aS}a z59%Nop7ZkjLX9KdqZp#oz75vmh?OUCpNBjb^T24V`5w$%|VRTUS2 z-ZeGg+mF?eN}g?9#RryumArNWsn!c2rDuugzwdjMUw>#rk4>G928e>nSl`tw>49EA zbd(~DzN8|GsDrwHLmPu-DH6dFgx<`ItoQ#VV+mB$D*$X$0FD#cW3(w0jl6!qH7>(q)%-?{U7+$Gv6*ggMJY* z5X6qBN!k|K!l6wUIMT2LBK^p{IemoD-Y77LiP$!EQMCtnUD4DSg#GkJ@o(e-ize52 z42#up=Y~J=v8TU*7tF%zi;!VM_cxt9jE6rtljG|Wq|?rT4Fx)E2x6+XrHI>B_)@ve z0KaPID7(Tzfi{>#EXwe>wI!yMqh>ek48r7U4I&uip!B3`JR0J)+`aKWKD7L6vk(N%hc+qud(-}Z zWXJbijVNolWE|iX@SE@(7A{nquK#nZ5nhpq0y*$8O_nIqZkDUZ&Y{9i;vM8%6Tz>i zbG2de@DxivaW-GR;zWO{z#-1^JJJX_7*&2!_UOHmLx(oT#Nsg~B<=OUW(W#sX3y~_ z6^UUv5y6-PcVb-QQ5~(MaonXq`Nc>jC(`HIXFF)P8BxN2Ty+xnfAlO)8kQv8H82xT zk0fGh;Fh8ut#A#xK9%6as>m}U;>}*N#P8`A+Kpfy#WP4R)jQ=7O2zYroyLik<9b~y z1N|Q^sib@U%B7R*dGfQf_~1pykRA?e$a^gcH2a6y88JI?<%2>XNrJcBh(Wp!=b`djd_4&pCjScuwu%|qLa@T`hi zK>zGULCAY~CZt9&Z^TUhU_D4h!4GnO^?~(VeCu+4@$~j$6ZgIt1DH#e42}o)7=(Wx5I;RzHSImd^64 zj@x|qt&@4=lV@|$NwuWY1q!YyHiJJB@YkR8ecKM89_5%sL8C|2q|Ee u|kUASb4 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 b9w`1F93{}M69$P_00000NkvXXu0mjfS|qb% literal 0 HcmV?d00001 diff --git a/core/common/src/main/res/mipmap-xhdpi/mifos_icon.png b/core/common/src/main/res/mipmap-xhdpi/mifos_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a5f1fe2c5a2845f44775ef19c495d53faddbce96 GIT binary patch literal 38929 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
  • I_t1A*~QqqZd-7Kv$B;%(IJUi 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

    ~UR1w{a(8F{pCg3{!C9h~Qx^5?og7 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%0D?RC%1^=J_t^0l8^`QXnfa=G# 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 literal 0 HcmV?d00001 diff --git a/core/common/src/main/res/mipmap-xxxhdpi/mifos_icon.png b/core/common/src/main/res/mipmap-xxxhdpi/mifos_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..83744a0b573cf915908e5295325b55af6650ebaf GIT binary patch literal 84947 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%0Ao^#1 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 YA3hg5tOk9C$p8QV07*qoM6N<$f@ri~Gynhq literal 0 HcmV?d00001 diff --git a/core/common/src/main/res/values/dimens.xml b/core/common/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..8f4bf16f67 --- /dev/null +++ b/core/common/src/main/res/values/dimens.xml @@ -0,0 +1,6 @@ + + + 25dp + 25dp + + \ No newline at end of file diff --git a/core/model/src/main/java/org/mifos/mobile/core/model/entity/AboutUsItem.kt b/core/model/src/main/java/org/mifos/mobile/core/model/entity/AboutUsItem.kt index d7508e2b17..ffc571189b 100644 --- a/core/model/src/main/java/org/mifos/mobile/core/model/entity/AboutUsItem.kt +++ b/core/model/src/main/java/org/mifos/mobile/core/model/entity/AboutUsItem.kt @@ -2,6 +2,7 @@ package org.mifos.mobile.core.model.entity import org.mifos.mobile.core.model.enums.AboutUsListItemId + data class AboutUsItem( val title: String?, val subtitle: Int? = null, diff --git a/feature/about/.gitignore b/feature/about/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/about/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/about/build.gradle.kts b/feature/about/build.gradle.kts new file mode 100644 index 0000000000..6673701063 --- /dev/null +++ b/feature/about/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) + alias(libs.plugins.kotlin.parcelize) +} + +android { + namespace = "org.mifos.mobile.feature.about" +} + +dependencies { + implementation(projects.core.model) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/about/consumer-rules.pro b/feature/about/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/about/proguard-rules.pro b/feature/about/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/about/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/about/src/androidTest/java/org/mifos/mobile/feature/about/ExampleInstrumentedTest.kt b/feature/about/src/androidTest/java/org/mifos/mobile/feature/about/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..a55a186f34 --- /dev/null +++ b/feature/about/src/androidTest/java/org/mifos/mobile/feature/about/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.about + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.about.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/about/src/main/AndroidManifest.xml b/feature/about/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/about/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/about/AboutUsHeader.kt b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt similarity index 91% rename from app/src/main/java/org/mifos/mobile/ui/about/AboutUsHeader.kt rename to feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt index ded1e8a65b..f375e83ea0 100644 --- a/app/src/main/java/org/mifos/mobile/ui/about/AboutUsHeader.kt +++ b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt @@ -1,7 +1,6 @@ -package org.mifos.mobile.ui.about +package org.mifos.mobile.feature.about.ui import androidx.compose.foundation.Image -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -11,13 +10,14 @@ import androidx.compose.material3.Text 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.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import org.mifos.mobile.R +import org.mifos.mobile.feature.about.R +@Preview @Composable fun AboutUsHeader() { Column { diff --git a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt new file mode 100644 index 0000000000..710fcef8ed --- /dev/null +++ b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt @@ -0,0 +1,97 @@ +package org.mifos.mobile.feature.about.ui + +import android.content.Context +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import org.mifos.mobile.core.model.entity.AboutUsItem +import org.mifos.mobile.core.model.enums.AboutUsListItemId +import org.mifos.mobile.core.ui.component.AboutUsItemCard +import org.mifos.mobile.core.ui.component.MifosItemCard +import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.feature.about.R +import java.util.Calendar + +@Composable +fun AboutUsScreen( + navigateToItem: (AboutUsItem) -> Unit +) { + val context = LocalContext.current + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + ) { + item { + Spacer(modifier = Modifier.height(48.dp)) + AboutUsHeader() + } + items(getAboutUsItem(context)) { item -> + MifosItemCard( + modifier = Modifier.padding(bottom = 8.dp), + onClick = { navigateToItem(item) } + ) { + item.title?.let { + AboutUsItemCard( + title = it, + subtitle = item.subtitle, + iconUrl = item.iconUrl + ) + } + } + } + } +} + +fun getAboutUsItem(context: Context): List { + + val currentYear = Calendar.getInstance().get(Calendar.YEAR) + val copyrightText = context.getString(R.string.copy_right_mifos).replace("%1\$s", currentYear.toString()) + + return listOf( + AboutUsItem( + title = context.getString(R.string.app_version_text), + itemId = AboutUsListItemId.APP_VERSION_TEXT + ), + AboutUsItem( + title = context.getString(R.string.official_website), + iconUrl = R.drawable.ic_website, + itemId = AboutUsListItemId.OFFICE_WEBSITE + ), + AboutUsItem( + title = context.getString(R.string.licenses), + iconUrl = R.drawable.ic_law_icon, + itemId = AboutUsListItemId.LICENSES + ), + AboutUsItem( + title = context.getString(R.string.privacy_policy), + iconUrl = R.drawable.ic_privacy_policy, + itemId = AboutUsListItemId.PRIVACY_POLICY + ), + AboutUsItem( + title = context.getString(R.string.sources), + iconUrl = R.drawable.ic_source_code, + itemId = AboutUsListItemId.SOURCE_CODE + ), + AboutUsItem( + title = copyrightText, + subtitle = R.string.license_string_with_value, + itemId = AboutUsListItemId.LICENSES_STRING_WITH_VALUE + ) + ) +} + +@Preview(showSystemUi = true, device = "id:pixel_5") +@Composable +fun AboutScreenPreview() { + MifosMobileTheme { + AboutUsScreen( + navigateToItem = {} + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_law_icon.xml b/feature/about/src/main/res/drawable/ic_law_icon.xml similarity index 100% rename from app/src/main/res/drawable/ic_law_icon.xml rename to feature/about/src/main/res/drawable/ic_law_icon.xml diff --git a/app/src/main/res/drawable/ic_privacy_policy.xml b/feature/about/src/main/res/drawable/ic_privacy_policy.xml similarity index 100% rename from app/src/main/res/drawable/ic_privacy_policy.xml rename to feature/about/src/main/res/drawable/ic_privacy_policy.xml diff --git a/app/src/main/res/drawable/ic_source_code.xml b/feature/about/src/main/res/drawable/ic_source_code.xml similarity index 91% rename from app/src/main/res/drawable/ic_source_code.xml rename to feature/about/src/main/res/drawable/ic_source_code.xml index 70566570bd..11838b8a25 100644 --- a/app/src/main/res/drawable/ic_source_code.xml +++ b/feature/about/src/main/res/drawable/ic_source_code.xml @@ -6,7 +6,7 @@ android:viewportWidth="48" android:viewportHeight="48"> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_website.xml b/feature/about/src/main/res/drawable/ic_website.xml similarity index 96% rename from app/src/main/res/drawable/ic_website.xml rename to feature/about/src/main/res/drawable/ic_website.xml index 097fc1bdec..13432f78e2 100644 --- a/app/src/main/res/drawable/ic_website.xml +++ b/feature/about/src/main/res/drawable/ic_website.xml @@ -6,6 +6,6 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/feature/about/src/main/res/values/strings.xml b/feature/about/src/main/res/values/strings.xml new file mode 100644 index 0000000000..3332af67a1 --- /dev/null +++ b/feature/about/src/main/res/values/strings.xml @@ -0,0 +1,14 @@ + + + Mifos Mobile + An Android Application built on top of the MifosX Self-Service platform for end-user customers to view/transact on the accounts and loans they hold. + ©2016-%1$s Mifos Initiative. + App version 1.0 + Official Website + Licenses + Privacy Policy + Source Code + License: MPL-2.0 + About Us + + \ No newline at end of file diff --git a/feature/about/src/test/java/org/mifos/mobile/feature/about/ExampleUnitTest.kt b/feature/about/src/test/java/org/mifos/mobile/feature/about/ExampleUnitTest.kt new file mode 100644 index 0000000000..556d2a2258 --- /dev/null +++ b/feature/about/src/test/java/org/mifos/mobile/feature/about/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.about + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/account/.gitignore b/feature/account/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/account/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/account/build.gradle.kts b/feature/account/build.gradle.kts new file mode 100644 index 0000000000..8358bc8032 --- /dev/null +++ b/feature/account/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + +android { + namespace = "org.mifos.mobile.feature.account" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + implementation(libs.reactivex.rxjava2.android) + implementation(libs.reactivex.rxjava2) + api(libs.androidx.compose.material) + implementation("com.github.rahul-gill.mifos-ui-library:uihouse:alpha-2.1") + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/account/consumer-rules.pro b/feature/account/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/account/proguard-rules.pro b/feature/account/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/account/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/account/src/main/AndroidManifest.xml b/feature/account/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/account/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/account/AccountsScreen.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/AccountsScreen.kt similarity index 91% rename from app/src/main/java/org/mifos/mobile/ui/account/AccountsScreen.kt rename to feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/AccountsScreen.kt index c2538be2ff..19a993684b 100644 --- a/app/src/main/java/org/mifos/mobile/ui/account/AccountsScreen.kt +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/AccountsScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.account +package org.mifos.mobile.feature.account.account.screens import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -16,7 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R +import org.mifos.mobile.feature.account.R import org.mifos.mobile.core.ui.component.MifosErrorComponent import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.common.Network @@ -28,6 +28,8 @@ import org.mifos.mobile.core.model.entity.accounts.savings.SavingAccount import org.mifos.mobile.core.model.entity.accounts.share.ShareAccount import org.mifos.mobile.core.ui.component.EmptyDataView import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.feature.account.utils.AccountState +import org.mifos.mobile.feature.account.viewmodel.AccountsViewModel @Composable @@ -101,7 +103,7 @@ fun AccountsScreen( @OptIn(ExperimentalMaterialApi::class) @Composable fun AccountsScreen( - uiState: AccountsUiState, + uiState: AccountState, onRetry: () -> Unit, isRefreshing: Boolean, onRefresh: () -> Unit, @@ -129,7 +131,7 @@ fun AccountsScreen( Box(modifier = Modifier.pullRefresh(pullRefreshState)) { when (uiState) { - is AccountsUiState.Error -> { + is AccountState.Error -> { MifosErrorComponent( isNetworkConnected = Network.isConnected(context), isRetryEnabled = true, @@ -137,11 +139,11 @@ fun AccountsScreen( ) } - is AccountsUiState.Loading -> { + is AccountState.Loading -> { MifosProgressIndicatorOverlay() } - is AccountsUiState.ShowSavingsAccounts -> { + is AccountState.ShowSavingsAccounts -> { if ((uiState.savingAccounts.isNullOrEmpty())) { EmptyDataView( icon = R.drawable.ic_error_black_24dp, @@ -160,7 +162,7 @@ fun AccountsScreen( } } - is AccountsUiState.ShowLoanAccounts -> { + is AccountState.ShowLoanAccounts -> { if ((uiState.loanAccounts.isNullOrEmpty())) { EmptyDataView( icon = R.drawable.ic_error_black_24dp, @@ -179,7 +181,7 @@ fun AccountsScreen( } } - is AccountsUiState.ShowShareAccounts -> { + is AccountState.ShowShareAccounts -> { if ((uiState.shareAccounts.isNullOrEmpty())) { EmptyDataView( icon = R.drawable.ic_error_black_24dp, @@ -207,22 +209,22 @@ fun AccountsScreen( } } -class AccountsScreenPreviewProvider : PreviewParameterProvider { +class AccountsScreenPreviewProvider : PreviewParameterProvider { - override val values: Sequence + override val values: Sequence get() = sequenceOf( - AccountsUiState.Loading, - AccountsUiState.Error, - AccountsUiState.ShowLoanAccounts(listOf()), - AccountsUiState.ShowShareAccounts(listOf()), - AccountsUiState.ShowSavingsAccounts(listOf()), + AccountState.Loading, + AccountState.Error, + AccountState.ShowLoanAccounts(listOf()), + AccountState.ShowShareAccounts(listOf()), + AccountState.ShowSavingsAccounts(listOf()), ) } @Preview(showSystemUi = true) @Composable private fun AccountSavingsScreenPreview( - @PreviewParameter(AccountsScreenPreviewProvider::class) accountUiState: AccountsUiState + @PreviewParameter(AccountsScreenPreviewProvider::class) accountUiState: AccountState ) { MifosMobileTheme { AccountsScreen( diff --git a/app/src/main/java/org/mifos/mobile/ui/account/LoanAccountContent.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/LoanAccountContent.kt similarity index 97% rename from app/src/main/java/org/mifos/mobile/ui/account/LoanAccountContent.kt rename to feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/LoanAccountContent.kt index f1be20ad05..a333988d1a 100644 --- a/app/src/main/java/org/mifos/mobile/ui/account/LoanAccountContent.kt +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/LoanAccountContent.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.account +package org.mifos.mobile.feature.account.account.screens import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -22,11 +22,12 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import org.mifos.mobile.R -import org.mifos.mobile.utils.AccountTypeItemIndicator +import org.mifos.mobile.feature.account.R +import org.mifos.mobile.feature.account.account.utils.AccountTypeItemIndicator import org.mifos.mobile.core.model.entity.accounts.loan.LoanAccount import org.mifos.mobile.core.common.utils.CurrencyUtil import org.mifos.mobile.core.common.utils.DateHelper + @Composable fun LoanAccountContent( accountsList: List, diff --git a/app/src/main/java/org/mifos/mobile/ui/account/SavingsAccountContent.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/SavingsAccountContent.kt similarity index 96% rename from app/src/main/java/org/mifos/mobile/ui/account/SavingsAccountContent.kt rename to feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/SavingsAccountContent.kt index 1827486824..dd13028396 100644 --- a/app/src/main/java/org/mifos/mobile/ui/account/SavingsAccountContent.kt +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/SavingsAccountContent.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.account +package org.mifos.mobile.feature.account.account.screens import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -22,8 +22,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import org.mifos.mobile.R -import org.mifos.mobile.utils.AccountTypeItemIndicator +import org.mifos.mobile.feature.account.R +import org.mifos.mobile.feature.account.account.utils.AccountTypeItemIndicator import org.mifos.mobile.core.model.entity.accounts.savings.SavingAccount import org.mifos.mobile.core.common.utils.CurrencyUtil import org.mifos.mobile.core.common.utils.DateHelper diff --git a/app/src/main/java/org/mifos/mobile/ui/account/ShareAccountContent.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/ShareAccountContent.kt similarity index 96% rename from app/src/main/java/org/mifos/mobile/ui/account/ShareAccountContent.kt rename to feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/ShareAccountContent.kt index a1902216bf..5586f19cfb 100644 --- a/app/src/main/java/org/mifos/mobile/ui/account/ShareAccountContent.kt +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/ShareAccountContent.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.account +package org.mifos.mobile.feature.account.account.screens import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -19,13 +19,12 @@ 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.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import org.mifos.mobile.R +import org.mifos.mobile.feature.account.R import org.mifos.mobile.core.model.entity.accounts.share.ShareAccount -import org.mifos.mobile.utils.AccountTypeItemIndicator +import org.mifos.mobile.feature.account.account.utils.AccountTypeItemIndicator @Composable fun AccountScreenShareContent( diff --git a/app/src/main/java/org/mifos/mobile/utils/AccountTypeItemIndicator.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountTypeItemIndicator.kt similarity index 97% rename from app/src/main/java/org/mifos/mobile/utils/AccountTypeItemIndicator.kt rename to feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountTypeItemIndicator.kt index a457b1cf53..dc398e899e 100644 --- a/app/src/main/java/org/mifos/mobile/utils/AccountTypeItemIndicator.kt +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountTypeItemIndicator.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.utils +package org.mifos.mobile.feature.account.account.utils import androidx.compose.foundation.Canvas import androidx.compose.foundation.background diff --git a/app/src/main/java/org/mifos/mobile/utils/AccountsFilterUtil.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountsFilterUtil.kt similarity index 92% rename from app/src/main/java/org/mifos/mobile/utils/AccountsFilterUtil.kt rename to feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountsFilterUtil.kt index 2a88c66173..bc40f2f96a 100644 --- a/app/src/main/java/org/mifos/mobile/utils/AccountsFilterUtil.kt +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountsFilterUtil.kt @@ -1,7 +1,7 @@ -package org.mifos.mobile.utils +package org.mifos.mobile.feature.account.account.utils import android.content.Context -import org.mifos.mobile.R +import org.mifos.mobile.feature.account.R data class AccountsFilterUtil( var activeString: String? = null, diff --git a/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsScreen.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/screens/ClientAccountsScreen.kt similarity index 91% rename from app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsScreen.kt rename to feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/screens/ClientAccountsScreen.kt index 5bf3494f1a..3562a7ffb0 100644 --- a/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountsScreen.kt +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/screens/ClientAccountsScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.client_accounts +package org.mifos.mobile.feature.account.client_account.screens import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.isSystemInDarkTheme @@ -21,15 +21,18 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R +import org.mifos.mobile.feature.account.R +import org.mifos.mobile.core.common.Constants import org.mifos.mobile.core.ui.component.FloatingActionButtonContent import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosIcons import org.mifos.mobile.core.ui.component.MifosTabPager import org.mifos.mobile.core.ui.theme.MifosMobileTheme -import org.mifos.mobile.ui.account.AccountsScreen -import org.mifos.mobile.ui.account.AccountsViewModel +import org.mifos.mobile.feature.account.account.screens.AccountsScreen import org.mifos.mobile.core.model.entity.CheckboxStatus +import org.mifos.mobile.feature.account.client_account.utils.ClientAccountFilterDialog +import org.mifos.mobile.feature.account.client_account.utils.ClientAccountsScreenTopBar +import org.mifos.mobile.feature.account.viewmodel.AccountsViewModel @Composable fun ClientAccountsScreen( @@ -133,7 +136,7 @@ fun ClientAccountsScreen( Icon( imageVector = MifosIcons.Add, contentDescription = "Create Account", - tint = MaterialTheme.colorScheme.onSurface + tint = if (isSystemInDarkTheme()) Color.Black else Color.White ) } ), @@ -193,7 +196,7 @@ fun ClientAccountsTabRow( ) { when (currentPage) { 0 -> AccountsScreen( - accountType = org.mifos.mobile.core.common.Constants.SAVINGS_ACCOUNTS, + accountType = Constants.SAVINGS_ACCOUNTS, onItemClick = { accType, accountId -> onItemClick.invoke( accType, @@ -203,7 +206,7 @@ fun ClientAccountsTabRow( ) 1 -> AccountsScreen( - accountType = org.mifos.mobile.core.common.Constants.LOAN_ACCOUNTS, + accountType = Constants.LOAN_ACCOUNTS, onItemClick = { accType, accountId -> onItemClick.invoke( accType, @@ -213,7 +216,7 @@ fun ClientAccountsTabRow( ) 2 -> AccountsScreen( - accountType = org.mifos.mobile.core.common.Constants.SHARE_ACCOUNTS, + accountType = Constants.SHARE_ACCOUNTS, onItemClick = { accType, accountId -> onItemClick.invoke( accType, diff --git a/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountFilterDialog.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountFilterDialog.kt similarity index 98% rename from app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountFilterDialog.kt rename to feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountFilterDialog.kt index 20485eef62..8d3cfd0346 100644 --- a/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountFilterDialog.kt +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountFilterDialog.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.client_accounts +package org.mifos.mobile.feature.account.client_account.utils import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement @@ -25,7 +25,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import org.mifos.mobile.R +import org.mifos.mobile.feature.account.R import org.mifos.mobile.core.model.entity.CheckboxStatus @Composable diff --git a/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountScreenTopBar.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountScreenTopBar.kt similarity index 94% rename from app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountScreenTopBar.kt rename to feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountScreenTopBar.kt index 5706ab76bc..635fade63c 100644 --- a/app/src/main/java/org/mifos/mobile/ui/client_accounts/ClientAccountScreenTopBar.kt +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountScreenTopBar.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.client_accounts +package org.mifos.mobile.feature.account.client_account.utils import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -94,7 +94,8 @@ fun ClientAccountsScreenTopBar( ) { Image( imageVector = MifosIcons.FilterList, - contentDescription = "Add account" + contentDescription = "Add account", + colorFilter = ColorFilter.tint(if (isSystemInDarkTheme()) Color.White else Color.Black) ) } } diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/AccountState.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/AccountState.kt new file mode 100644 index 0000000000..c02058197e --- /dev/null +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/AccountState.kt @@ -0,0 +1,14 @@ +package org.mifos.mobile.feature.account.utils + +import org.mifos.mobile.core.model.entity.accounts.loan.LoanAccount +import org.mifos.mobile.core.model.entity.accounts.savings.SavingAccount +import org.mifos.mobile.core.model.entity.accounts.share.ShareAccount + + +sealed class AccountState { + data object Error : AccountState() + data object Loading : AccountState() + data class ShowSavingsAccounts(val savingAccounts: List?) : AccountState() + data class ShowLoanAccounts(val loanAccounts: List?) : AccountState() + data class ShowShareAccounts(val shareAccounts: List?) : AccountState() +} \ No newline at end of file diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/StatusUtils.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/StatusUtils.kt new file mode 100644 index 0000000000..8fadffdb24 --- /dev/null +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/StatusUtils.kt @@ -0,0 +1,127 @@ +package org.mifos.mobile.feature.account.utils + +import android.content.Context +import androidx.core.content.ContextCompat +import org.mifos.mobile.feature.account.R +import org.mifos.mobile.core.model.entity.CheckboxStatus +import org.mifos.mobile.ui.getThemeAttributeColor + +/** + * Created by dilpreet on 3/7/17. + */ +object StatusUtils { + + fun getSavingsAccountStatusList(context: Context?): List { + val arrayList = ArrayList() + arrayList.add( + CheckboxStatus( + context?.getString(R.string.active), + ContextCompat.getColor(context!!, R.color.deposit_green), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.approved), + ContextCompat.getColor(context, R.color.light_green), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.approval_pending), + ContextCompat + .getColor(context, R.color.light_yellow), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.matured), + ContextCompat.getColor(context, R.color.red_light), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.closed), + context.getThemeAttributeColor(R.attr.colorOnSurface), + ), + )or + return arrayList + } + + fun getLoanAccountStatusList(context: Context?): List { + val arrayList = ArrayList() + arrayList.add( + CheckboxStatus( + context?.getString(R.string.in_arrears), + ContextCompat.getColor(context!!, R.color.red), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.active), + ContextCompat.getColor(context, R.color.deposit_green), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.waiting_for_disburse), + ContextCompat.getColor(context, R.color.blue), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.approval_pending), + ContextCompat + .getColor(context, R.color.light_yellow), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.overpaid), + ContextCompat.getColor(context, R.color.purple), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.closed), + context.getThemeAttributeColor(R.attr.colorOnSurface), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.withdrawn), + context.getThemeAttributeColor(R.attr.colorOnSurfaceVariant), + ), + ) + return arrayList + } + + fun getShareAccountStatusList(context: Context?): List { + val arrayList = ArrayList() + arrayList.add( + CheckboxStatus( + context?.getString(R.string.active), + ContextCompat.getColor(context!!, R.color.deposit_green), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.approved), + ContextCompat.getColor(context, R.color.light_green), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.approval_pending), + ContextCompat + .getColor(context, R.color.light_yellow), + ), + ) + arrayList.add( + CheckboxStatus( + context.getString(R.string.closed), + ContextCompat.getColor(context, R.color.light_blue), + ), + ) + return arrayList + } +} diff --git a/app/src/main/java/org/mifos/mobile/ui/account/AccountsViewModel.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/viewmodel/AccountsViewModel.kt similarity index 89% rename from app/src/main/java/org/mifos/mobile/ui/account/AccountsViewModel.kt rename to feature/account/src/main/java/org/mifos/mobile/feature/account/viewmodel/AccountsViewModel.kt index d07f90e379..ef31d42c38 100644 --- a/app/src/main/java/org/mifos/mobile/ui/account/AccountsViewModel.kt +++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/viewmodel/AccountsViewModel.kt @@ -1,10 +1,9 @@ -package org.mifos.mobile.ui.account +package org.mifos.mobile.feature.account.viewmodel import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import io.reactivex.Observable import io.reactivex.functions.Predicate import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -13,14 +12,15 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.mifos.mobile.core.common.Constants -import org.mifos.mobile.utils.AccountsFilterUtil +import org.mifos.mobile.feature.account.account.utils.AccountsFilterUtil import org.mifos.mobile.core.data.repositories.AccountsRepository import org.mifos.mobile.core.data.repositories.HomeRepository import org.mifos.mobile.core.model.entity.CheckboxStatus import org.mifos.mobile.core.model.entity.accounts.loan.LoanAccount import org.mifos.mobile.core.model.entity.accounts.savings.SavingAccount import org.mifos.mobile.core.model.entity.accounts.share.ShareAccount -import org.mifos.mobile.utils.StatusUtils +import org.mifos.mobile.feature.account.utils.AccountState +import org.mifos.mobile.feature.account.utils.StatusUtils import java.util.* import javax.inject.Inject @@ -30,8 +30,8 @@ class AccountsViewModel @Inject constructor( private val homeRepositoryImp: HomeRepository ) : ViewModel() { - private val _accountsUiState = MutableStateFlow(AccountsUiState.Loading) - val accountsUiState: StateFlow = _accountsUiState + private val _accountsUiState = MutableStateFlow(AccountState.Loading) + val accountsUiState: StateFlow = _accountsUiState private val _isRefreshing = MutableStateFlow(false) val isRefreshing: StateFlow get() = _isRefreshing.asStateFlow() @@ -174,16 +174,16 @@ class AccountsViewModel @Inject constructor( */ fun loadClientAccounts() { viewModelScope.launch { - _accountsUiState.value = AccountsUiState.Loading + _accountsUiState.value = AccountState.Loading homeRepositoryImp.clientAccounts().catch { - _accountsUiState.value = AccountsUiState.Error + _accountsUiState.value = AccountState.Error }.collect { clientAccounts -> _accountsUiState.value = - AccountsUiState.ShowSavingsAccounts(clientAccounts.savingsAccounts) + AccountState.ShowSavingsAccounts(clientAccounts.savingsAccounts) _accountsUiState.value = - AccountsUiState.ShowLoanAccounts(clientAccounts.loanAccounts) + AccountState.ShowLoanAccounts(clientAccounts.loanAccounts) _accountsUiState.value = - AccountsUiState.ShowShareAccounts(clientAccounts.shareAccounts) + AccountState.ShowShareAccounts(clientAccounts.shareAccounts) } } } @@ -196,17 +196,17 @@ class AccountsViewModel @Inject constructor( */ fun loadAccounts(accountType: String?) { viewModelScope.launch { - _accountsUiState.value = AccountsUiState.Loading + _accountsUiState.value = AccountState.Loading accountsRepositoryImp.loadAccounts(accountType).catch { - _accountsUiState.value = AccountsUiState.Error + _accountsUiState.value = AccountState.Error }.collect { clientAccounts -> when (accountType) { Constants.SAVINGS_ACCOUNTS -> _accountsUiState.value = - AccountsUiState.ShowSavingsAccounts(clientAccounts.savingsAccounts) + AccountState.ShowSavingsAccounts(clientAccounts.savingsAccounts) Constants.LOAN_ACCOUNTS -> _accountsUiState.value = - AccountsUiState.ShowLoanAccounts(clientAccounts.loanAccounts) + AccountState.ShowLoanAccounts(clientAccounts.loanAccounts) Constants.SHARE_ACCOUNTS -> _accountsUiState.value = - AccountsUiState.ShowShareAccounts(clientAccounts.shareAccounts) + AccountState.ShowShareAccounts(clientAccounts.shareAccounts) } _isRefreshing.emit(false) } @@ -225,7 +225,7 @@ class AccountsViewModel @Inject constructor( accounts: List?, input: String?, ): List { - return Observable.fromIterable(accounts) + return io.reactivex.Observable.fromIterable(accounts) .filter { (accountNo, productName) -> input?.lowercase(Locale.ROOT) ?.let { productName?.lowercase(Locale.ROOT)?.contains(it) } == true || @@ -247,7 +247,7 @@ class AccountsViewModel @Inject constructor( accounts: List?, input: String?, ): List { - return Observable.fromIterable(accounts) + return io.reactivex.Observable.fromIterable(accounts) .filter { (_, _, _, accountNo, productName) -> input?.lowercase(Locale.ROOT) ?.let { productName?.lowercase(Locale.ROOT)?.contains(it) } == true || @@ -269,7 +269,7 @@ class AccountsViewModel @Inject constructor( accounts: Collection?, input: String?, ): List { - return Observable.fromIterable(accounts) + return io.reactivex.Observable.fromIterable(accounts) .filter { (accountNo, _, _, _, productName) -> input?.lowercase(Locale.ROOT) ?.let { productName?.lowercase(Locale.ROOT)?.contains(it) } == true || @@ -287,7 +287,7 @@ class AccountsViewModel @Inject constructor( * `checkboxStatus.isChecked()` as true. */ fun getCheckedStatus(statusModelList: List?): List? { - return Observable.fromIterable(statusModelList) + return io.reactivex.Observable.fromIterable(statusModelList) .filter { (_, _, isChecked) -> isChecked }.toList().blockingGet() } @@ -303,7 +303,7 @@ class AccountsViewModel @Inject constructor( status: CheckboxStatus?, accountsFilterUtil: AccountsFilterUtil ): Collection { - return Observable.fromIterable(accounts) + return io.reactivex.Observable.fromIterable(accounts) .filter( Predicate { (_, _, _, _, _, _, _, _, _, _, _, status1) -> if (accountsFilterUtil.activeString @@ -344,7 +344,7 @@ class AccountsViewModel @Inject constructor( status: CheckboxStatus?, accountsFilterUtil: AccountsFilterUtil ): Collection { - return Observable.fromIterable(accounts) + return io.reactivex.Observable.fromIterable(accounts) .filter( Predicate { (_, _, _, _, _, _, _, _, _, _, _, status1, _, _, _, _, _, inArrears) -> if (accountsFilterUtil.inArrearsString?.let { status?.status?.compareTo(it) } @@ -393,7 +393,7 @@ class AccountsViewModel @Inject constructor( status: CheckboxStatus?, accountsFilterUtil: AccountsFilterUtil ): Collection { - return Observable.fromIterable(accounts) + return io.reactivex.Observable.fromIterable(accounts) .filter( Predicate { (_, _, _, _, _, _, status1) -> if (accountsFilterUtil.activeString @@ -419,12 +419,4 @@ class AccountsViewModel @Inject constructor( ).toList().blockingGet().filterNotNull() } -} - -sealed class AccountsUiState { - data object Error : AccountsUiState() - data object Loading : AccountsUiState() - data class ShowSavingsAccounts(val savingAccounts: List?) : AccountsUiState() - data class ShowLoanAccounts(val loanAccounts: List?) : AccountsUiState() - data class ShowShareAccounts(val shareAccounts: List?) : AccountsUiState() } \ No newline at end of file diff --git a/feature/account/src/main/res/drawable/ic_error_black_24dp.xml b/feature/account/src/main/res/drawable/ic_error_black_24dp.xml new file mode 100644 index 0000000000..b666c18189 --- /dev/null +++ b/feature/account/src/main/res/drawable/ic_error_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/account/src/main/res/values/colors.xml b/feature/account/src/main/res/values/colors.xml new file mode 100644 index 0000000000..324b9cd910 --- /dev/null +++ b/feature/account/src/main/res/values/colors.xml @@ -0,0 +1,74 @@ + + + #ffffffff + #000000 + #eaeaea + #ff14c416 + #ff8bf98a + #fff9ac06 + #FF87DBF9 + #fff9393c + #ffd1d1d1 + #8ad3da44 + #8ada6134 + #ff003fff + #FF0000 + #1C1C1C + + #B2C1C8 + + + #ff33b5e5 + + #33999999 + + #BB666666 + + #ff99cc00 + + #ffff4444 + + #ff0099cc + + #ff669900 + + #ffcc0000 + + #ffaa66cc + + #ffffbb33 + + #ffff8800 + + #ff00ddff + + #33CCCCCC + + + + + + + + #03A9F4 + #0288D1 + #B3E5FC + #FF4081 + #212121 + #757575 + #FFFFFF + #BDBDBD + #ffffff + @color/blue_light + #EEEEEE + #00000000 + + + + @color/blue_light + @color/green_light + @color/red_light + @color/orange_light + + + \ No newline at end of file diff --git a/feature/account/src/main/res/values/strings.xml b/feature/account/src/main/res/values/strings.xml new file mode 100644 index 0000000000..412bde3542 --- /dev/null +++ b/feature/account/src/main/res/values/strings.xml @@ -0,0 +1,37 @@ + + + Active + Approved + Approval Pending + Matured + Waiting for Disburse + Overpaid + Closed + Withdrawn + In Arrears + There is no SavingsAccount associated with you + There is no LoanAccount associated with you + There is no ShareAccount associated with you + Disbursement + Submitted + %1$s %2$s + Pending + Savings Account + Share Account + Loan Account + Saving + Loan + Share + Select all filters you want to apply + Clear Filters + Cancel + Filter + Deposit + Dividend Payout + Withdrawal + Interest Posting + Fee Deduction + Withdrawal Transfer + Rejected Transfer + Overdraft Fee + \ No newline at end of file diff --git a/feature/beneficiary/build.gradle.kts b/feature/beneficiary/build.gradle.kts index 025ffb95dc..710d66ca7c 100644 --- a/feature/beneficiary/build.gradle.kts +++ b/feature/beneficiary/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } android { - namespace = "org.mifos.mobile.feature.guarantor" + namespace = "org.mifos.mobile.feature.beneficiary" } dependencies { diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationContent.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationContent.kt index 9ce3c9c1b2..edb33238a1 100644 --- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationContent.kt +++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationContent.kt @@ -29,7 +29,7 @@ import org.mifos.mobile.core.ui.component.MifosDropDownTextField import org.mifos.mobile.core.ui.component.MifosOutlinedTextField import org.mifos.mobile.core.ui.component.MifosTextButton import org.mifos.mobile.core.ui.theme.MifosMobileTheme -import org.mifos.mobile.feature.guarantor.R +import org.mifos.mobile.feature.beneficiary.R @Composable fun BeneficiaryApplicationContent( diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationScreen.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationScreen.kt index 37015c1635..4d0ee0d958 100644 --- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationScreen.kt +++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationScreen.kt @@ -23,7 +23,7 @@ import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary import org.mifos.mobile.core.model.entity.beneficiary.BeneficiaryPayload import org.mifos.mobile.core.model.entity.templates.beneficiary.BeneficiaryTemplate import org.mifos.mobile.core.model.enums.BeneficiaryState -import org.mifos.mobile.feature.guarantor.R +import org.mifos.mobile.feature.beneficiary.R @Composable diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationViewModel.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationViewModel.kt index bc06da0f6e..6d7f0cb20f 100644 --- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationViewModel.kt +++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationViewModel.kt @@ -13,7 +13,7 @@ import org.mifos.mobile.core.model.entity.beneficiary.BeneficiaryPayload import org.mifos.mobile.core.model.entity.beneficiary.BeneficiaryUpdatePayload import org.mifos.mobile.core.model.entity.templates.beneficiary.BeneficiaryTemplate import org.mifos.mobile.core.model.enums.BeneficiaryState -import org.mifos.mobile.feature.guarantor.R +import org.mifos.mobile.feature.beneficiary.R import javax.inject.Inject @HiltViewModel diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailContent.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailContent.kt index ed47164706..8b5ccb4bf7 100644 --- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailContent.kt +++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailContent.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.unit.dp import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary import org.mifos.mobile.core.ui.component.MifosTitleDescSingleLineEqual import org.mifos.mobile.core.ui.theme.MifosMobileTheme -import org.mifos.mobile.feature.guarantor.R +import org.mifos.mobile.feature.beneficiary.R @Composable fun BeneficiaryDetailContent( diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailScreen.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailScreen.kt index 67db6459f9..765403d378 100644 --- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailScreen.kt +++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailScreen.kt @@ -35,7 +35,7 @@ import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary import org.mifos.mobile.core.ui.component.MifosAlertDialog import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.theme.MifosMobileTheme -import org.mifos.mobile.feature.guarantor.R +import org.mifos.mobile.feature.beneficiary.R @Composable fun BeneficiaryDetailScreen( diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailViewModel.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailViewModel.kt index 6c1bcea5da..d2bf93e0ee 100644 --- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailViewModel.kt +++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailViewModel.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch import org.mifos.mobile.core.data.repositories.BeneficiaryRepository import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary -import org.mifos.mobile.feature.guarantor.R +import org.mifos.mobile.feature.beneficiary.R import javax.inject.Inject @HiltViewModel diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_list/BeneficiaryListScreen.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_list/BeneficiaryListScreen.kt index d3d9444891..4489d38b01 100644 --- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_list/BeneficiaryListScreen.kt +++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_list/BeneficiaryListScreen.kt @@ -31,7 +31,7 @@ import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.common.Network import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary -import org.mifos.mobile.feature.guarantor.R +import org.mifos.mobile.feature.beneficiary.R @Composable fun BeneficiaryListScreen( diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreen.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreen.kt index f187bf22ee..04bd23476f 100644 --- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreen.kt +++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreen.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.mifos.mobile.core.ui.component.MifosTopBar import org.mifos.mobile.core.ui.theme.MifosMobileTheme -import org.mifos.mobile.feature.guarantor.R +import org.mifos.mobile.feature.beneficiary.R @RequiresApi(Build.VERSION_CODES.TIRAMISU) diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreenIcons.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreenIcons.kt index be4736baca..1873011dc2 100644 --- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreenIcons.kt +++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreenIcons.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.mifos.mobile.core.ui.theme.MifosMobileTheme -import org.mifos.mobile.feature.guarantor.R +import org.mifos.mobile.feature.beneficiary.R import org.mifos.mobile.ui.beneficiary.presentation.RenderIconAndText @Composable diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/RenderIconAndText.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/RenderIconAndText.kt index 66a61d4dde..430173002e 100644 --- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/RenderIconAndText.kt +++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/RenderIconAndText.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.mifos.mobile.core.ui.theme.MifosMobileTheme -import org.mifos.mobile.feature.guarantor.R +import org.mifos.mobile.feature.beneficiary.R /** diff --git a/feature/client_charge/.gitignore b/feature/client_charge/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/client_charge/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/client_charge/build.gradle.kts b/feature/client_charge/build.gradle.kts new file mode 100644 index 0000000000..92bf869e9d --- /dev/null +++ b/feature/client_charge/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + +android { + namespace = "org.mifos.mobile.feature.client_charge" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + implementation(libs.dbflow) + kapt(libs.dbflow.processor) + implementation(libs.dbflow.core) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/client_charge/consumer-rules.pro b/feature/client_charge/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/client_charge/proguard-rules.pro b/feature/client_charge/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/client_charge/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/client_charge/src/androidTest/java/org/mifos/mobile/feature/client_charge/ExampleInstrumentedTest.kt b/feature/client_charge/src/androidTest/java/org/mifos/mobile/feature/client_charge/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..4a0a30ee5d --- /dev/null +++ b/feature/client_charge/src/androidTest/java/org/mifos/mobile/feature/client_charge/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.client_charge + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.client_charge.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/client_charge/src/main/AndroidManifest.xml b/feature/client_charge/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/client_charge/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/client_charge/ClientChargeScreen.kt b/feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/screens/ClientChargeScreen.kt similarity index 91% rename from app/src/main/java/org/mifos/mobile/ui/client_charge/ClientChargeScreen.kt rename to feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/screens/ClientChargeScreen.kt index def8e0e31b..a0e28f63dc 100644 --- a/app/src/main/java/org/mifos/mobile/ui/client_charge/ClientChargeScreen.kt +++ b/feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/screens/ClientChargeScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.client_charge +package org.mifos.mobile.feature.client_charge.screens import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -27,7 +27,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R +import org.mifos.mobile.feature.client_charge.R import org.mifos.mobile.core.ui.component.EmptyDataView import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosErrorComponent @@ -40,7 +40,8 @@ import org.mifos.mobile.core.common.utils.CurrencyUtil import org.mifos.mobile.core.common.utils.DateHelper import org.mifos.mobile.core.common.Network import org.mifos.mobile.core.datastore.model.Charge - +import org.mifos.mobile.feature.client_charge.utils.ClientChargeState +import org.mifos.mobile.feature.client_charge.viewmodel.ClientChargeViewModel @Composable fun ClientChargeScreen( @@ -57,7 +58,7 @@ fun ClientChargeScreen( @Composable fun ClientChargeScreen( - uiState: ClientChargeUiState, + uiState: ClientChargeState, navigateBack: () -> Unit, onRetry: () -> Unit ) { @@ -72,11 +73,11 @@ fun ClientChargeScreen( .fillMaxSize() ) { when (uiState) { - is ClientChargeUiState.Loading -> { + is ClientChargeState.Loading -> { MifosProgressIndicator() } - is ClientChargeUiState.Error -> { + is ClientChargeState.Error -> { MifosErrorComponent( isNetworkConnected = Network.isConnected(context), isRetryEnabled = true, @@ -84,7 +85,7 @@ fun ClientChargeScreen( ) } - is ClientChargeUiState.Success -> { + is ClientChargeState.Success -> { if (uiState.charges.isEmpty()) { EmptyDataView( modifier = Modifier.fillMaxSize(), @@ -197,7 +198,7 @@ fun ClientChargeItem( @Preview(showSystemUi = true) @Composable fun ClientChargeScreenPreview( - @PreviewParameter(ClientChargeUiStatesPreviews::class) uiState: ClientChargeUiState + @PreviewParameter(ClientChargeUiStatesPreviews::class) uiState: ClientChargeState ) { MifosMobileTheme { ClientChargeScreen( @@ -208,11 +209,11 @@ fun ClientChargeScreenPreview( } } -class ClientChargeUiStatesPreviews : PreviewParameterProvider { - override val values: Sequence +class ClientChargeUiStatesPreviews : PreviewParameterProvider { + override val values: Sequence get() = sequenceOf( - ClientChargeUiState.Success(listOf(Charge(), Charge())), - ClientChargeUiState.Error(""), - ClientChargeUiState.Loading, + ClientChargeState.Success(listOf(Charge(), Charge())), + ClientChargeState.Error(""), + ClientChargeState.Loading, ) } diff --git a/feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/utils/ClientChargeState.kt b/feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/utils/ClientChargeState.kt new file mode 100644 index 0000000000..32dd66bb8d --- /dev/null +++ b/feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/utils/ClientChargeState.kt @@ -0,0 +1,9 @@ +package org.mifos.mobile.feature.client_charge.utils + +import org.mifos.mobile.core.datastore.model.Charge + +sealed class ClientChargeState { + data object Loading : ClientChargeState() + data class Error(val message: String?) : ClientChargeState() + data class Success(val charges: List) : ClientChargeState() +} diff --git a/app/src/main/java/org/mifos/mobile/ui/client_charge/ClientChargeViewModel.kt b/feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/viewmodel/ClientChargeViewModel.kt similarity index 64% rename from app/src/main/java/org/mifos/mobile/ui/client_charge/ClientChargeViewModel.kt rename to feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/viewmodel/ClientChargeViewModel.kt index e25b8eaa3d..741131f9e5 100644 --- a/app/src/main/java/org/mifos/mobile/ui/client_charge/ClientChargeViewModel.kt +++ b/feature/client_charge/src/main/java/org/mifos/mobile/feature/client_charge/viewmodel/ClientChargeViewModel.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.client_charge +package org.mifos.mobile.feature.client_charge.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -9,14 +9,15 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch import org.mifos.mobile.core.data.repositories.ClientChargeRepository import org.mifos.mobile.core.datastore.model.Charge +import org.mifos.mobile.feature.client_charge.utils.ClientChargeState import javax.inject.Inject @HiltViewModel class ClientChargeViewModel @Inject constructor(private val clientChargeRepositoryImp: ClientChargeRepository) : ViewModel() { - private val _clientChargeUiState = MutableStateFlow(ClientChargeUiState.Loading) - val clientChargeUiState: StateFlow get() = _clientChargeUiState + private val _clientChargeUiState = MutableStateFlow(ClientChargeState.Loading) + val clientChargeUiState: StateFlow get() = _clientChargeUiState private val _clientId = MutableStateFlow(null) private val clientId: StateFlow get() = _clientId @@ -46,51 +47,46 @@ class ClientChargeViewModel @Inject constructor(private val clientChargeReposito private fun loadClientCharges(clientId: Long) { viewModelScope.launch { - _clientChargeUiState.value = ClientChargeUiState.Loading + _clientChargeUiState.value = ClientChargeState.Loading clientChargeRepositoryImp.getClientCharges(clientId).catch { - _clientChargeUiState.value = ClientChargeUiState.Error(it.message) + _clientChargeUiState.value = ClientChargeState.Error(it.message) }.collect { - _clientChargeUiState.value = ClientChargeUiState.Success(it.pageItems) + _clientChargeUiState.value = ClientChargeState.Success(it.pageItems) } } } private fun loadLoanAccountCharges(loanId: Long) { viewModelScope.launch { - _clientChargeUiState.value = ClientChargeUiState.Loading + _clientChargeUiState.value = ClientChargeState.Loading clientChargeRepositoryImp.getLoanCharges(loanId).catch { - _clientChargeUiState.value = ClientChargeUiState.Error(it.message) + _clientChargeUiState.value = ClientChargeState.Error(it.message) }.collect { - _clientChargeUiState.value = ClientChargeUiState.Success(it) + _clientChargeUiState.value = ClientChargeState.Success(it) } } } private fun loadSavingsAccountCharges(savingsId: Long) { viewModelScope.launch { - _clientChargeUiState.value = ClientChargeUiState.Loading + _clientChargeUiState.value = ClientChargeState.Loading clientChargeRepositoryImp.getSavingsCharges(savingsId).catch { - _clientChargeUiState.value = ClientChargeUiState.Error(it.message) + _clientChargeUiState.value = ClientChargeState.Error(it.message) }.collect { - _clientChargeUiState.value = ClientChargeUiState.Success(it) + _clientChargeUiState.value = ClientChargeState.Success(it) } } } fun loadClientLocalCharges() { viewModelScope.launch { - _clientChargeUiState.value = ClientChargeUiState.Loading + _clientChargeUiState.value = ClientChargeState.Loading clientChargeRepositoryImp.clientLocalCharges().catch { - _clientChargeUiState.value = ClientChargeUiState.Error(it.message) + _clientChargeUiState.value = ClientChargeState.Error(it.message) }.collect { - _clientChargeUiState.value = ClientChargeUiState.Success(it.pageItems.filterNotNull()) + _clientChargeUiState.value = + ClientChargeState.Success(it.pageItems.filterNotNull()) } } } -} - -sealed class ClientChargeUiState { - data object Loading : ClientChargeUiState() - data class Error(val message: String?) : ClientChargeUiState() - data class Success(val charges: List) : ClientChargeUiState() -} +} \ No newline at end of file diff --git a/feature/client_charge/src/main/res/drawable/ic_charges.xml b/feature/client_charge/src/main/res/drawable/ic_charges.xml new file mode 100644 index 0000000000..dc27ed8afe --- /dev/null +++ b/feature/client_charge/src/main/res/drawable/ic_charges.xml @@ -0,0 +1,46 @@ + + + + + + + + + + diff --git a/feature/client_charge/src/main/res/values/strings.xml b/feature/client_charge/src/main/res/values/strings.xml new file mode 100644 index 0000000000..65b7cb1147 --- /dev/null +++ b/feature/client_charge/src/main/res/values/strings.xml @@ -0,0 +1,11 @@ + + + + Client Charges + No charges found + Due: + %1$s %2$s + Outstanding: + Waved: + Paid: + \ No newline at end of file diff --git a/feature/client_charge/src/test/java/org/mifos/mobile/feature/client_charge/ExampleUnitTest.kt b/feature/client_charge/src/test/java/org/mifos/mobile/feature/client_charge/ExampleUnitTest.kt new file mode 100644 index 0000000000..3aaceaa194 --- /dev/null +++ b/feature/client_charge/src/test/java/org/mifos/mobile/feature/client_charge/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.client_charge + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/help/.gitignore b/feature/help/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/help/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/help/build.gradle.kts b/feature/help/build.gradle.kts new file mode 100644 index 0000000000..ae9464c86e --- /dev/null +++ b/feature/help/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + +android { + namespace = "org.mifos.mobile.feature.help" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/help/consumer-rules.pro b/feature/help/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/help/proguard-rules.pro b/feature/help/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/help/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/help/src/androidTest/java/org/mifos/mobile/feature/help/ExampleInstrumentedTest.kt b/feature/help/src/androidTest/java/org/mifos/mobile/feature/help/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..b036810469 --- /dev/null +++ b/feature/help/src/androidTest/java/org/mifos/mobile/feature/help/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.help + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.help.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/help/src/main/AndroidManifest.xml b/feature/help/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/help/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/help/HelpScreen.kt b/feature/help/src/main/java/org/mifos/mobile/feature/help/HelpScreen.kt similarity index 59% rename from app/src/main/java/org/mifos/mobile/ui/help/HelpScreen.kt rename to feature/help/src/main/java/org/mifos/mobile/feature/help/HelpScreen.kt index e532453034..32dac88207 100644 --- a/app/src/main/java/org/mifos/mobile/ui/help/HelpScreen.kt +++ b/feature/help/src/main/java/org/mifos/mobile/feature/help/HelpScreen.kt @@ -1,6 +1,6 @@ -package org.mifos.mobile.ui.help +package org.mifos.mobile.feature.help -import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize @@ -15,23 +15,58 @@ import androidx.compose.material.icons.outlined.Mail import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import org.mifos.mobile.R +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.mifos.mobile.core.model.entity.FAQ import org.mifos.mobile.core.ui.component.EmptyDataView import org.mifos.mobile.core.ui.component.FaqItemHolder +import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosTextButtonWithTopDrawable import org.mifos.mobile.core.ui.component.MifosTitleSearchCard import org.mifos.mobile.core.ui.component.MifosTopBar + @Composable fun HelpScreen( - faqArrayList: List?, - selectedFaqPosition: Int = -1, + viewModel: HelpViewModel = hiltViewModel(), + callNow: () -> Unit, + leaveEmail: () -> Unit, + findLocations: () -> Unit, + navigateBack: () -> Unit, +) { + + val context = LocalContext.current + val uiState by viewModel.helpUiState.collectAsStateWithLifecycle() + + LaunchedEffect(key1 = Unit) { + viewModel.loadFaq( + context.resources?.getStringArray(R.array.faq_qs), + context.resources?.getStringArray(R.array.faq_ans) + ) + } + + HelpScreen( + uiState = uiState, + callNow = callNow, + leaveEmail = leaveEmail, + findLocations = findLocations, + navigateBack = navigateBack, + searchQuery = { viewModel.filterList(it) }, + onSearchDismiss = { viewModel.loadFaq(qs = null, ans = null) }, + updateFaqPosition = { viewModel.updateSelectedFaqPosition(it) } + ) +} + +@Composable +fun HelpScreen( + uiState: HelpUiState, callNow: () -> Unit, leaveEmail: () -> Unit, findLocations: () -> Unit, @@ -40,21 +75,55 @@ fun HelpScreen( onSearchDismiss: () -> Unit, updateFaqPosition: (Int) -> Unit, ) { + + MFScaffold( + topBar = { + MifosTopBar( + navigateBack = navigateBack, + title = { + MifosTitleSearchCard( + searchQuery = searchQuery, + titleResourceId = R.string.help, + onSearchDismiss = onSearchDismiss + ) + } + ) + }, + scaffoldContent = { paddingValues -> + Box(modifier = Modifier.padding(paddingValues)) { + when(uiState) { + is HelpUiState.Initial -> Unit + is HelpUiState.ShowFaq -> { + HelpContent( + faqArrayList = uiState.faqArrayList.toList().filterNotNull(), + selectedFaqPosition = uiState.selectedFaqPosition, + callNow = callNow, + leaveEmail = leaveEmail, + findLocations = findLocations, + updateFaqPosition = updateFaqPosition + ) + } + } + } + } + ) + +} + + +@Composable +fun HelpContent( + faqArrayList: List, + selectedFaqPosition: Int, + callNow: () -> Unit, + leaveEmail: () -> Unit, + findLocations: () -> Unit, + updateFaqPosition: (Int) -> Unit, +) { Column( modifier = Modifier .fillMaxSize() ) { - MifosTopBar( - navigateBack = navigateBack, - title = { - MifosTitleSearchCard( - searchQuery = searchQuery, - titleResourceId = R.string.help, - onSearchDismiss = onSearchDismiss - ) - } - ) - Text( text = stringResource(id = R.string.faq), modifier = Modifier @@ -116,11 +185,8 @@ fun HelpScreen( } else { EmptyDataView( modifier = Modifier.fillMaxSize(), - icon = R.drawable.ic_help_black_24dp, error = R.string.no_questions_found ) } - } } - diff --git a/app/src/main/java/org/mifos/mobile/ui/help/HelpViewModel.kt b/feature/help/src/main/java/org/mifos/mobile/feature/help/HelpViewModel.kt similarity index 88% rename from app/src/main/java/org/mifos/mobile/ui/help/HelpViewModel.kt rename to feature/help/src/main/java/org/mifos/mobile/feature/help/HelpViewModel.kt index 402fef46f8..2cd7b7403b 100644 --- a/app/src/main/java/org/mifos/mobile/ui/help/HelpViewModel.kt +++ b/feature/help/src/main/java/org/mifos/mobile/feature/help/HelpViewModel.kt @@ -1,11 +1,10 @@ -package org.mifos.mobile.ui.help +package org.mifos.mobile.feature.help import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import org.mifos.mobile.core.model.entity.FAQ -import org.mifos.mobile.utils.HelpUiState import java.util.Locale import javax.inject.Inject @@ -14,6 +13,7 @@ class HelpViewModel @Inject constructor() : ViewModel() { private val _helpUiState = MutableStateFlow(HelpUiState.Initial) val helpUiState: StateFlow get() = _helpUiState + private var allFaqList: ArrayList? = null fun loadFaq(qs: Array?, ans: Array?) { @@ -53,4 +53,12 @@ class HelpViewModel @Inject constructor() : ViewModel() { _helpUiState.value = currentState.copy(selectedFaqPosition = selectFaqPosition) } } +} + +sealed class HelpUiState { + data object Initial : HelpUiState() + data class ShowFaq( + val faqArrayList: ArrayList, + val selectedFaqPosition: Int = -1 + ) : HelpUiState() } \ No newline at end of file diff --git a/feature/help/src/main/res/values/strings.xml b/feature/help/src/main/res/values/strings.xml new file mode 100644 index 0000000000..5ad3a18161 --- /dev/null +++ b/feature/help/src/main/res/values/strings.xml @@ -0,0 +1,44 @@ + + + Help + Frequently Asked Questions\n + Call now + Leave an email + Find Locations + No Questions Found + + + How do I apply for new loan account? + Where can I view my profile information? + Where can I see my Savings Accounts Transactions? + What is the use of QR Code? + How to create a beneficiary using QR Code? + How to make a payment for a Loan Account? + + + + To apply for loan account, click on \"Apply for Loan\" given on the Home Screen. + + You can view your profile information by clicking on the User Image present on Home + Screen. + + To view your savings account transactions, navigate to the Accounts Sections, click on + the required savings account, click on three dots present on top right and select + Transactions option. + + QrCode for any loan or savings accounts can be shared with other users which will + allow them to create a beneficiary. + + In order to create a Beneficiary, goto Beneficiary option from Home Screen then click + on the circular button present on bottom right, choose the option to Scan which opens + your device camera, scan the QR Code of other person for which you want to create a + beneficiary, after filling in the other required detail you will be able to create a + Beneficiary using QR Code. + + To make a payment for a loan account, navigate to the Accounts Sections, choose LOAN + then open your required Loan Account and click on Make Payment option. + + + + + \ No newline at end of file diff --git a/feature/help/src/test/java/org/mifos/mobile/feature/help/ExampleUnitTest.kt b/feature/help/src/test/java/org/mifos/mobile/feature/help/ExampleUnitTest.kt new file mode 100644 index 0000000000..c57167b0bc --- /dev/null +++ b/feature/help/src/test/java/org/mifos/mobile/feature/help/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.help + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/loan/src/main/java/org/mifos/mobile/feature/loan/loan_repayment_schedule/LoanRepaymentScheduleViewModel.kt b/feature/loan/src/main/java/org/mifos/mobile/feature/loan/loan_repayment_schedule/LoanRepaymentScheduleViewModel.kt index 824f76f34c..95086b8be5 100644 --- a/feature/loan/src/main/java/org/mifos/mobile/feature/loan/loan_repayment_schedule/LoanRepaymentScheduleViewModel.kt +++ b/feature/loan/src/main/java/org/mifos/mobile/feature/loan/loan_repayment_schedule/LoanRepaymentScheduleViewModel.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope import org.mifos.mobile.core.data.repositories.LoanRepository import org.mifos.mobile.core.model.entity.accounts.loan.LoanWithAssociations import org.mifos.mobile.core.model.entity.templates.loans.LoanTemplate diff --git a/feature/loan/src/main/res/drawable/ic_check_circle_green_24px.xml b/feature/loan/src/main/res/drawable/ic_check_circle_green_24px.xml index e9c6a620c0..f6880695d8 100644 --- a/feature/loan/src/main/res/drawable/ic_check_circle_green_24px.xml +++ b/feature/loan/src/main/res/drawable/ic_check_circle_green_24px.xml @@ -14,7 +14,7 @@ android:name="tick" android:pathData="M6,11 l0,0 l0,0" android:strokeWidth="1" - android:strokeColor="@color/white" /> + android:strokeColor="#ffffff" /> \ No newline at end of file diff --git a/feature/loan/src/main/res/values/colors.xml b/feature/loan/src/main/res/values/colors.xml new file mode 100644 index 0000000000..324b9cd910 --- /dev/null +++ b/feature/loan/src/main/res/values/colors.xml @@ -0,0 +1,74 @@ + + + #ffffffff + #000000 + #eaeaea + #ff14c416 + #ff8bf98a + #fff9ac06 + #FF87DBF9 + #fff9393c + #ffd1d1d1 + #8ad3da44 + #8ada6134 + #ff003fff + #FF0000 + #1C1C1C + + #B2C1C8 + + + #ff33b5e5 + + #33999999 + + #BB666666 + + #ff99cc00 + + #ffff4444 + + #ff0099cc + + #ff669900 + + #ffcc0000 + + #ffaa66cc + + #ffffbb33 + + #ffff8800 + + #ff00ddff + + #33CCCCCC + + + + + + + + #03A9F4 + #0288D1 + #B3E5FC + #FF4081 + #212121 + #757575 + #FFFFFF + #BDBDBD + #ffffff + @color/blue_light + #EEEEEE + #00000000 + + + + @color/blue_light + @color/green_light + @color/red_light + @color/orange_light + + + \ No newline at end of file diff --git a/feature/location/.gitignore b/feature/location/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/location/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/location/build.gradle.kts b/feature/location/build.gradle.kts new file mode 100644 index 0000000000..8414ba6866 --- /dev/null +++ b/feature/location/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + +android { + namespace = "org.mifos.mobile.feature.location" +} + +dependencies { + // google maps + implementation(libs.google.map.compose) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/location/consumer-rules.pro b/feature/location/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/location/proguard-rules.pro b/feature/location/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/location/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/location/src/androidTest/java/org/mifos/mobile/feature/location/ExampleInstrumentedTest.kt b/feature/location/src/androidTest/java/org/mifos/mobile/feature/location/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..690bff5f44 --- /dev/null +++ b/feature/location/src/androidTest/java/org/mifos/mobile/feature/location/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.location + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.location.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/location/src/main/AndroidManifest.xml b/feature/location/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/location/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/location/LocationScreen.kt b/feature/location/src/main/java/org/mifos/mobile/feature/location/LocationScreen.kt similarity index 95% rename from app/src/main/java/org/mifos/mobile/ui/location/LocationScreen.kt rename to feature/location/src/main/java/org/mifos/mobile/feature/location/LocationScreen.kt index 4b2a4c02d6..f29ab9933a 100644 --- a/app/src/main/java/org/mifos/mobile/ui/location/LocationScreen.kt +++ b/feature/location/src/main/java/org/mifos/mobile/feature/location/LocationScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.location +package org.mifos.mobile.feature.location import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -14,7 +14,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView -import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.MapView import com.google.android.gms.maps.model.CameraPosition import com.google.android.gms.maps.model.LatLng @@ -22,7 +21,6 @@ import com.google.maps.android.compose.GoogleMap import com.google.maps.android.compose.Marker import com.google.maps.android.compose.MarkerState import com.google.maps.android.compose.rememberCameraPositionState -import org.mifos.mobile.R import org.mifos.mobile.core.ui.theme.MifosMobileTheme import kotlin.coroutines.coroutineContext diff --git a/feature/location/src/main/res/values/strings.xml b/feature/location/src/main/res/values/strings.xml new file mode 100644 index 0000000000..7843e450cc --- /dev/null +++ b/feature/location/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + + Mifos Initiative + Mifos Initiative , Seattle , Washington 98121 + Seattle + Home to large tech industry + \ No newline at end of file diff --git a/feature/location/src/test/java/org/mifos/mobile/feature/location/ExampleUnitTest.kt b/feature/location/src/test/java/org/mifos/mobile/feature/location/ExampleUnitTest.kt new file mode 100644 index 0000000000..a3c7ee53d0 --- /dev/null +++ b/feature/location/src/test/java/org/mifos/mobile/feature/location/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.location + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/login/.gitignore b/feature/login/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/login/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/login/build.gradle.kts b/feature/login/build.gradle.kts new file mode 100644 index 0000000000..41dd4f420b --- /dev/null +++ b/feature/login/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + +android { + namespace = "org.mifos.mobile.feature.login" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/login/consumer-rules.pro b/feature/login/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/login/proguard-rules.pro b/feature/login/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/login/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/login/src/androidTest/java/org/mifos/mobile/feature/login/ExampleInstrumentedTest.kt b/feature/login/src/androidTest/java/org/mifos/mobile/feature/login/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..c1e24c3c3f --- /dev/null +++ b/feature/login/src/androidTest/java/org/mifos/mobile/feature/login/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.login + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.login.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/login/src/main/AndroidManifest.xml b/feature/login/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/login/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/login/LoginScreen.kt b/feature/login/src/main/java/org/mifos/mobile/feature/login/screens/LoginScreen.kt similarity index 52% rename from app/src/main/java/org/mifos/mobile/ui/login/LoginScreen.kt rename to feature/login/src/main/java/org/mifos/mobile/feature/login/screens/LoginScreen.kt index b6cfcdbf55..d0b9b185fc 100644 --- a/app/src/main/java/org/mifos/mobile/ui/login/LoginScreen.kt +++ b/feature/login/src/main/java/org/mifos/mobile/feature/login/screens/LoginScreen.kt @@ -1,9 +1,12 @@ -package org.mifos.mobile.ui.login +package org.mifos.mobile.feature.login.screens -import android.content.res.Configuration.UI_MODE_NIGHT_NO +import android.content.Context +import android.content.res.Configuration +import android.widget.Toast import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.gestures.detectVerticalDragGestures import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row @@ -24,18 +27,22 @@ import androidx.compose.material3.Divider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType @@ -43,21 +50,103 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import org.mifos.mobile.R +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import org.mifos.mobile.feature.login.R +import org.mifos.mobile.core.common.Network +import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosMobileIcon import org.mifos.mobile.core.ui.component.MifosOutlinedTextField +import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.feature.login.utils.LoginState +import org.mifos.mobile.feature.login.viewmodel.LoginViewModel -@OptIn(ExperimentalComposeUiApi::class) @Composable fun LoginScreen( + viewModel: LoginViewModel = hiltViewModel(), + startRegisterActivity: () -> Unit, + startPassCodeActivity: () -> Unit, +) { + + val context = LocalContext.current + val uiState by viewModel.loginUiState.collectAsStateWithLifecycle() + + LoginScreen( + uiState = uiState, + loadClient = { viewModel.loadClient() }, + startPassCodeActivity = startPassCodeActivity, + startRegisterActivity = startRegisterActivity, + login = { username, password -> + if (Network.isConnected(context)) { + if (isCredentialsValid(username, password)) + viewModel.login(username, password) + } else { + Toast.makeText(context, context.getString(R.string.no_internet_connection), Toast.LENGTH_SHORT).show() + } + } + ) +} + +@Composable +fun LoginScreen( + uiState: LoginState, + loadClient: () -> Unit, + startPassCodeActivity: () -> Unit, + startRegisterActivity: () -> Unit, + login: ( username: String, password: String ) -> Unit +) { + val context = LocalContext.current + + Box( + modifier = Modifier + .fillMaxSize() + ) { + + LoginContent( + login= login, + createAccount = startRegisterActivity + ) + + when (uiState) { + + LoginState.Initial -> Unit + + is LoginState.Error -> { + LaunchedEffect(key1 = true) { + Toast.makeText(context, context.getString(R.string.login_failed), Toast.LENGTH_SHORT).show() + } + } + + LoginState.Loading -> { + MifosProgressIndicatorOverlay() + } + + is LoginState.LoginSuccess -> { + loadClient.invoke() + } + + is LoginState.LoadClientSuccess -> { + startPassCodeActivity.invoke() + LaunchedEffect(key1 = true) { + Toast.makeText(context, context.getString(R.string.toast_welcome, uiState.clientName), Toast.LENGTH_SHORT).show() + } + } + } + } +} + + +@Composable +fun LoginContent( login: (username: String, password: String) -> Unit, - createAccount: () -> Unit, - getUsernameError: (String) -> String, - getPasswordError: (String) -> String + createAccount: () -> Unit ) { + val context = LocalContext.current val keyboardController = LocalSoftwareKeyboardController.current var username by rememberSaveable(stateSaver = TextFieldValue.Saver) { @@ -75,10 +164,8 @@ fun LoginScreen( var usernameError by rememberSaveable { mutableStateOf(false) } var passwordError by rememberSaveable { mutableStateOf(false) } - - val usernameErrorContent = getUsernameError.invoke(username.text) - val passwordErrorContent = getPasswordError.invoke(password.text) - + val usernameErrorContent = validateUsername(username.text, context) + val passwordErrorContent = validatePassword(password.text, context) Column( modifier = Modifier @@ -215,10 +302,128 @@ fun LoginScreen( } } -@Preview(showSystemUi = true, device = "id:pixel_5") +fun isFieldEmpty(fieldText: String): Boolean { + return fieldText.isEmpty() +} + +fun isUsernameLengthInadequate(username: String): Boolean { + return username.length < 5 +} + +fun isPasswordLengthInadequate(password: String): Boolean { + return password.length < 6 +} + +fun usernameHasSpaces(username: String): Boolean { + return username.trim().contains(" ") +} + +private fun isCredentialsValid(username: String, password: String): Boolean { + var credentialValid = true + when { + isFieldEmpty(username) -> { + credentialValid = false + } + + isUsernameLengthInadequate(username) -> { + credentialValid = false + } + + usernameHasSpaces(username) -> { + credentialValid = false + } + } + + when { + isFieldEmpty(password) -> { + credentialValid = false + } + + isPasswordLengthInadequate(password) -> { + credentialValid = false + } + } + return credentialValid +} + +private fun validateUsername(username: String, context: Context): String { + var usernameError = "" + when { + isFieldEmpty(username) -> { + usernameError = context.getString( + R.string.error_validation_blank, + context.getString(R.string.username), + ).toString() + } + + isUsernameLengthInadequate(username) -> { + usernameError = context.getString( + R.string.error_validation_minimum_chars, + context.getString(R.string.username), + context.resources?.getInteger(R.integer.username_minimum_length), + ).toString() + } + + usernameHasSpaces(username) -> { + usernameError = context.getString( + R.string.error_validation_cannot_contain_spaces, + context.getString(R.string.username), + context.getString(R.string.not_contain_username), + ).toString() + } + } + return usernameError +} + +private fun validatePassword(password: String, context: Context): String { + var passwordError = "" + when { + isFieldEmpty(password) -> { + passwordError = context.getString( + R.string.error_validation_blank, + context.getString(R.string.password), + ).toString() + } + + isPasswordLengthInadequate(password) -> { + passwordError = context.getString( + R.string.error_validation_minimum_chars, + context.getString(R.string.password), + context.resources.getInteger(R.integer.password_minimum_length), + ).toString() + } + } + return passwordError +} + +class LoginScreenPreviewProvider : PreviewParameterProvider { + + override val values: Sequence + get() = sequenceOf( + LoginState.Loading, + LoginState.Error, + LoginState.LoginSuccess, + LoginState.LoadClientSuccess(""), + LoginState.Initial, + ) +} + +private fun showToast(context: Context, text : String){ + Toast.makeText(context, text, Toast.LENGTH_LONG).show() +} + +@Preview(showSystemUi = true) @Composable -fun LoanScreenPreview() { +private fun LoginScreenPreview( + @PreviewParameter(LoginScreenPreviewProvider::class) loginUiState: LoginState +) { MifosMobileTheme { - LoginScreen({ _, _ -> }, {}, { "" }, { "" }) + LoginScreen( + loginUiState, + {}, + {}, + {}, + { _, _ -> } + ) } -} +} \ No newline at end of file diff --git a/feature/login/src/main/java/org/mifos/mobile/feature/login/utils/LoginState.kt b/feature/login/src/main/java/org/mifos/mobile/feature/login/utils/LoginState.kt new file mode 100644 index 0000000000..1068ca3ac4 --- /dev/null +++ b/feature/login/src/main/java/org/mifos/mobile/feature/login/utils/LoginState.kt @@ -0,0 +1,9 @@ +package org.mifos.mobile.feature.login.utils + +sealed class LoginState { + object Initial : LoginState() + object LoginSuccess : LoginState() + object Loading : LoginState() + object Error : LoginState() + data class LoadClientSuccess(val clientName: String?) : LoginState() +} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/login/LoginViewModel.kt b/feature/login/src/main/java/org/mifos/mobile/feature/login/viewmodel/LoginViewModel.kt similarity index 65% rename from app/src/main/java/org/mifos/mobile/ui/login/LoginViewModel.kt rename to feature/login/src/main/java/org/mifos/mobile/feature/login/viewmodel/LoginViewModel.kt index a61c5a58d3..040fc74652 100644 --- a/app/src/main/java/org/mifos/mobile/ui/login/LoginViewModel.kt +++ b/feature/login/src/main/java/org/mifos/mobile/feature/login/viewmodel/LoginViewModel.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.login +package org.mifos.mobile.feature.login.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch import org.mifos.mobile.core.data.repositories.ClientRepository import org.mifos.mobile.core.data.repositories.UserAuthRepository -import org.mifos.mobile.utils.LoginUiState +import org.mifos.mobile.feature.login.utils.LoginState import javax.inject.Inject @HiltViewModel @@ -19,24 +19,8 @@ class LoginViewModel @Inject constructor( ) : ViewModel() { - private var _loginUiState = MutableStateFlow(LoginUiState.Initial) - val loginUiState: StateFlow get() = _loginUiState - - fun isFieldEmpty(fieldText: String): Boolean { - return fieldText.isEmpty() - } - - fun isUsernameLengthInadequate(username: String): Boolean { - return username.length < 5 - } - - fun isPasswordLengthInadequate(password: String): Boolean { - return password.length < 6 - } - - fun usernameHasSpaces(username: String): Boolean { - return username.trim().contains(" ") - } + private var _loginUiState = MutableStateFlow(LoginState.Initial) + val loginUiState: StateFlow get() = _loginUiState /** * This method attempts to authenticate the user from @@ -45,12 +29,12 @@ class LoginViewModel @Inject constructor( */ fun login(username: String, password: String) { viewModelScope.launch { - _loginUiState.value = LoginUiState.Loading + _loginUiState.value = LoginState.Loading userAuthRepositoryImp.login(username, password).catch { - _loginUiState.value = LoginUiState.Error + _loginUiState.value = LoginState.Error }.collect { clientRepositoryImp.saveAuthenticationTokenForSession(it) - _loginUiState.value = LoginUiState.LoginSuccess + _loginUiState.value = LoginState.LoginSuccess } } } @@ -61,7 +45,7 @@ class LoginViewModel @Inject constructor( fun loadClient() { viewModelScope.launch { clientRepositoryImp.loadClient().catch { - _loginUiState.value = LoginUiState.Error + _loginUiState.value = LoginState.Error clientRepositoryImp.clearPrefHelper() clientRepositoryImp.reInitializeService() }.collect { @@ -70,7 +54,7 @@ class LoginViewModel @Inject constructor( val clientName = it.pageItems[0].displayName clientRepositoryImp.setClientId(clientId) clientRepositoryImp.reInitializeService() - _loginUiState.value = LoginUiState.LoadClientSuccess(clientName) + _loginUiState.value = LoginState.LoadClientSuccess(clientName) } } } diff --git a/app/src/main/res/drawable/ic_lock_black_24dp.xml b/feature/login/src/main/res/drawable/ic_lock_black_24dp.xml similarity index 100% rename from app/src/main/res/drawable/ic_lock_black_24dp.xml rename to feature/login/src/main/res/drawable/ic_lock_black_24dp.xml diff --git a/feature/login/src/main/res/drawable/ic_person_black_24dp.xml b/feature/login/src/main/res/drawable/ic_person_black_24dp.xml new file mode 100644 index 0000000000..55495d5a08 --- /dev/null +++ b/feature/login/src/main/res/drawable/ic_person_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/login/src/main/res/drawable/mifos_logo.png b/feature/login/src/main/res/drawable/mifos_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..39bef3caf150b0a43215c933e3798a3d1fa6d60e GIT binary patch literal 60004 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

    ^E8+GaYYC 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 + + + No Internet Connection + Register + Login Failed, Please Try Again Later. + Welcome %1$s + Username + Password + Login + Create an account + %1$s cannot be blank + %1$s cannot be less than %2$d characters + %1$s cannot contain %2$s + Spaces + \ No newline at end of file diff --git a/feature/login/src/main/res/values/validations.xml b/feature/login/src/main/res/values/validations.xml new file mode 100644 index 0000000000..37e3b680ab --- /dev/null +++ b/feature/login/src/main/res/values/validations.xml @@ -0,0 +1,5 @@ + + + 5 + 6 + \ No newline at end of file diff --git a/feature/login/src/test/java/org/mifos/mobile/feature/login/ExampleUnitTest.kt b/feature/login/src/test/java/org/mifos/mobile/feature/login/ExampleUnitTest.kt new file mode 100644 index 0000000000..337817e5b3 --- /dev/null +++ b/feature/login/src/test/java/org/mifos/mobile/feature/login/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.login + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/notification/.gitignore b/feature/notification/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/notification/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/notification/build.gradle.kts b/feature/notification/build.gradle.kts new file mode 100644 index 0000000000..aea537f979 --- /dev/null +++ b/feature/notification/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + +android { + namespace = "org.mifos.mobile.feature.notification" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + implementation(projects.core.datastore) + + // DBFlow + implementation(libs.dbflow) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/notification/consumer-rules.pro b/feature/notification/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/notification/proguard-rules.pro b/feature/notification/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/notification/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/notification/src/androidTest/java/org/mifos/mobile/feature/notification/ExampleInstrumentedTest.kt b/feature/notification/src/androidTest/java/org/mifos/mobile/feature/notification/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..35fb3c53b6 --- /dev/null +++ b/feature/notification/src/androidTest/java/org/mifos/mobile/feature/notification/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.notification + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.notification.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/notification/src/main/AndroidManifest.xml b/feature/notification/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/notification/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/notification/NotificationScreen.kt b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationScreen.kt similarity index 99% rename from app/src/main/java/org/mifos/mobile/ui/notification/NotificationScreen.kt rename to feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationScreen.kt index 8c2974228c..7aa3d96dca 100644 --- a/app/src/main/java/org/mifos/mobile/ui/notification/NotificationScreen.kt +++ b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.notification +package org.mifos.mobile.feature.notification import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -36,7 +36,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.EmptyDataView import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosErrorComponent diff --git a/app/src/main/java/org/mifos/mobile/ui/notification/NotificationViewModel.kt b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationViewModel.kt similarity index 96% rename from app/src/main/java/org/mifos/mobile/ui/notification/NotificationViewModel.kt rename to feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationViewModel.kt index 3bab85ace1..f79dec4898 100644 --- a/app/src/main/java/org/mifos/mobile/ui/notification/NotificationViewModel.kt +++ b/feature/notification/src/main/java/org/mifos/mobile/feature/notification/NotificationViewModel.kt @@ -1,6 +1,5 @@ -package org.mifos.mobile.ui.notification +package org.mifos.mobile.feature.notification -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel diff --git a/app/src/main/res/drawable/ic_notifications.xml b/feature/notification/src/main/res/drawable/ic_notifications.xml similarity index 91% rename from app/src/main/res/drawable/ic_notifications.xml rename to feature/notification/src/main/res/drawable/ic_notifications.xml index 0d92e0f33f..94190607de 100644 --- a/app/src/main/res/drawable/ic_notifications.xml +++ b/feature/notification/src/main/res/drawable/ic_notifications.xml @@ -5,6 +5,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/feature/notification/src/main/res/values/strings.xml b/feature/notification/src/main/res/values/strings.xml new file mode 100644 index 0000000000..c3b1528c4a --- /dev/null +++ b/feature/notification/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + + Notification + OK + No Notification + + \ No newline at end of file diff --git a/feature/notification/src/test/java/org/mifos/mobile/feature/notification/ExampleUnitTest.kt b/feature/notification/src/test/java/org/mifos/mobile/feature/notification/ExampleUnitTest.kt new file mode 100644 index 0000000000..cf9b3e8f3b --- /dev/null +++ b/feature/notification/src/test/java/org/mifos/mobile/feature/notification/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.notification + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/qr/.gitignore b/feature/qr/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/qr/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/qr/build.gradle.kts b/feature/qr/build.gradle.kts new file mode 100644 index 0000000000..38522d0dfe --- /dev/null +++ b/feature/qr/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + +android { + namespace = "org.mifos.mobile.feature.qr" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + implementation(projects.core.logs) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) + + //cameraX + implementation(libs.androidx.camera.camera2) + implementation(libs.androidx.camera.lifecycle) + implementation(libs.androidx.camera.view) + implementation(libs.androidx.camera.core) + + //qr code + implementation("com.google.zxing:core:3.5.2") + implementation("me.dm7.barcodescanner:zxing:1.9.13") + + //gson + implementation(libs.squareup.retrofit.converter.gson) + + //image cropper + implementation("com.github.CanHub:Android-Image-Cropper:4.0.0") + + //guava for ListenableFuture + implementation ("com.google.guava:guava:33.0.0-android") + +} \ No newline at end of file diff --git a/feature/qr/consumer-rules.pro b/feature/qr/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/qr/proguard-rules.pro b/feature/qr/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/qr/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/qr/src/androidTest/java/org/mifos/mobile/feature/qr/ExampleInstrumentedTest.kt b/feature/qr/src/androidTest/java/org/mifos/mobile/feature/qr/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..2f61ea3705 --- /dev/null +++ b/feature/qr/src/androidTest/java/org/mifos/mobile/feature/qr/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.qr + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.qr.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/qr/src/main/AndroidManifest.xml b/feature/qr/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/qr/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/qr/BarcodeCamera.kt b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/qr/BarcodeCamera.kt similarity index 96% rename from app/src/main/java/org/mifos/mobile/ui/qr/BarcodeCamera.kt rename to feature/qr/src/main/java/org/mifos/mobile/feature/qr/qr/BarcodeCamera.kt index d62b1cf407..37896df70b 100644 --- a/app/src/main/java/org/mifos/mobile/ui/qr/BarcodeCamera.kt +++ b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/qr/BarcodeCamera.kt @@ -1,9 +1,7 @@ -package org.mifos.mobile.ui.qr +package org.mifos.mobile.feature.qr.qr -import android.Manifest import android.content.ContentValues.TAG import android.content.Context -import android.os.Build import androidx.camera.core.Camera import android.util.Log import android.view.ViewGroup @@ -22,7 +20,7 @@ import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.viewinterop.AndroidView import androidx.core.content.ContextCompat import androidx.lifecycle.LifecycleOwner -import org.mifos.mobile.utils.QrCodeAnalyzer +import org.mifos.mobile.feature.qr.utils.QrCodeAnalyzer @ExperimentalGetImage class BarcodeCamera { diff --git a/app/src/main/java/org/mifos/mobile/ui/qr/QrCodeReaderScreen.kt b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/qr/QrCodeReaderScreen.kt similarity index 97% rename from app/src/main/java/org/mifos/mobile/ui/qr/QrCodeReaderScreen.kt rename to feature/qr/src/main/java/org/mifos/mobile/feature/qr/qr/QrCodeReaderScreen.kt index 1bdee5a01a..8d27e3f36c 100644 --- a/app/src/main/java/org/mifos/mobile/ui/qr/QrCodeReaderScreen.kt +++ b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/qr/QrCodeReaderScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.qr +package org.mifos.mobile.feature.qr.qr import androidx.annotation.OptIn import androidx.camera.core.ExperimentalGetImage @@ -16,10 +16,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosIcons import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.feature.qr.R @Composable fun QrCodeReaderScreen( diff --git a/app/src/main/java/org/mifos/mobile/ui/qr_code_display/QrCodeDisplayScreen.kt b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/qr_code_display/QrCodeDisplayScreen.kt similarity index 96% rename from app/src/main/java/org/mifos/mobile/ui/qr_code_display/QrCodeDisplayScreen.kt rename to feature/qr/src/main/java/org/mifos/mobile/feature/qr/qr_code_display/QrCodeDisplayScreen.kt index dde35a4b00..29ccbf7b1a 100644 --- a/app/src/main/java/org/mifos/mobile/ui/qr_code_display/QrCodeDisplayScreen.kt +++ b/feature/qr/src/main/java/org/mifos/mobile/feature/qr/qr_code_display/QrCodeDisplayScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.qr_code_display +package org.mifos.mobile.feature.qr.qr_code_display import android.content.Context import android.content.Intent @@ -30,13 +30,13 @@ import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat.startActivity import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosErrorComponent import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.component.MifosTopBar import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.common.utils.Utils +import org.mifos.mobile.feature.qr.R @Composable @@ -143,7 +143,7 @@ class QrCodeDisplayScreenPreviewProvider : PreviewParameterProvider + + Add Beneficiary + QR Code + Choose option + Please grant us storage permission in settings. + + Permission denied to storage + Grant permission + Deny + No image seleted or Something went wrong + Selected QR image + You have denied permission to + write to storage, without this permission you will not be able to add beneficiaries using QR Code. + Are you sure you want to deny this permission? + + Proceed + Import QR + Error while reading QR, make sure you select proper region + + \ No newline at end of file diff --git a/feature/qr/src/test/java/org/mifos/mobile/feature/qr/ExampleUnitTest.kt b/feature/qr/src/test/java/org/mifos/mobile/feature/qr/ExampleUnitTest.kt new file mode 100644 index 0000000000..fe3b0784cb --- /dev/null +++ b/feature/qr/src/test/java/org/mifos/mobile/feature/qr/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.qr + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/recent_transaction/.gitignore b/feature/recent_transaction/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/recent_transaction/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/recent_transaction/build.gradle.kts b/feature/recent_transaction/build.gradle.kts new file mode 100644 index 0000000000..9a549b9a57 --- /dev/null +++ b/feature/recent_transaction/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + +android { + namespace = "org.mifos.mobile.feature.recent_transaction" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/recent_transaction/consumer-rules.pro b/feature/recent_transaction/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/recent_transaction/proguard-rules.pro b/feature/recent_transaction/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/recent_transaction/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/recent_transaction/src/androidTest/java/org/mifos/mobile/feature/recent_transaction/ExampleInstrumentedTest.kt b/feature/recent_transaction/src/androidTest/java/org/mifos/mobile/feature/recent_transaction/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..1103c0e0f1 --- /dev/null +++ b/feature/recent_transaction/src/androidTest/java/org/mifos/mobile/feature/recent_transaction/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.recent_transaction + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.recent_transaction.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/recent_transaction/src/main/AndroidManifest.xml b/feature/recent_transaction/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/recent_transaction/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/recent_transactions/RecentTransactionScreen.kt b/feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/screens/RecentTransactionScreen.kt similarity index 91% rename from app/src/main/java/org/mifos/mobile/ui/recent_transactions/RecentTransactionScreen.kt rename to feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/screens/RecentTransactionScreen.kt index 2dbd419864..4db1e9eff5 100644 --- a/app/src/main/java/org/mifos/mobile/ui/recent_transactions/RecentTransactionScreen.kt +++ b/feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/screens/RecentTransactionScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.recent_transactions +package org.mifos.mobile.feature.recent_transaction.screens import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement @@ -35,8 +35,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.MifosSelfServiceApp -import org.mifos.mobile.R +import org.mifos.mobile.feature.recent_transaction.R import org.mifos.mobile.core.ui.component.EmptyDataView import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosErrorComponent @@ -48,6 +47,8 @@ import org.mifos.mobile.core.common.utils.DateHelper import org.mifos.mobile.core.common.Network import org.mifos.mobile.core.model.entity.Transaction import org.mifos.mobile.core.common.utils.Utils +import org.mifos.mobile.feature.recent_transaction.utils.RecentTransactionState +import org.mifos.mobile.feature.recent_transaction.viewmodel.RecentTransactionViewModel @Composable fun RecentTransactionScreen( @@ -76,7 +77,7 @@ fun RecentTransactionScreen( @OptIn(ExperimentalMaterial3Api::class) @Composable fun RecentTransactionScreen( - uiState: RecentTransactionUiState, + uiState: RecentTransactionState, navigateBack: () -> Unit, onRetry: () -> Unit, isRefreshing: Boolean, @@ -104,7 +105,7 @@ fun RecentTransactionScreen( ) { when (uiState) { - is RecentTransactionUiState.Error -> { + is RecentTransactionState.Error -> { MifosErrorComponent( isNetworkConnected = Network.isConnected(context), isRetryEnabled = true, @@ -112,11 +113,11 @@ fun RecentTransactionScreen( ) } - is RecentTransactionUiState.Loading -> { + is RecentTransactionState.Loading -> { MifosProgressIndicatorOverlay() } - is RecentTransactionUiState.Success -> { + is RecentTransactionState.Success -> { if (uiState.transactions.isEmpty()) { EmptyDataView( icon = R.drawable.ic_error_black_24dp, @@ -192,6 +193,9 @@ fun RecentTransactionsContent( @Composable fun RecentTransactionListItem(transaction: Transaction?) { + + val context = LocalContext.current + Row(modifier = Modifier.padding(8.dp), verticalAlignment = Alignment.CenterVertically) { Image( painter = painterResource(id = R.drawable.ic_local_atm_black_24dp), @@ -213,7 +217,7 @@ fun RecentTransactionListItem(transaction: Transaction?) { id = R.string.string_and_string, transaction?.currency?.displaySymbol ?: transaction?.currency?.code ?: "", CurrencyUtil.formatCurrency( - MifosSelfServiceApp.context, + context, transaction?.amount ?: 0.0, ) ), @@ -235,19 +239,19 @@ fun RecentTransactionListItem(transaction: Transaction?) { } } -class RecentTransactionScreenPreviewProvider : PreviewParameterProvider { - override val values: Sequence +class RecentTransactionScreenPreviewProvider : PreviewParameterProvider { + override val values: Sequence get() = sequenceOf( - RecentTransactionUiState.Loading, - RecentTransactionUiState.Error(""), - RecentTransactionUiState.Success(listOf(), canPaginate = true) + RecentTransactionState.Loading, + RecentTransactionState.Error(""), + RecentTransactionState.Success(listOf(), canPaginate = true) ) } @Preview(showSystemUi = true) @Composable private fun RecentTransactionScreenPreview( - @PreviewParameter(RecentTransactionScreenPreviewProvider::class) recentTransactionUiState: RecentTransactionUiState + @PreviewParameter(RecentTransactionScreenPreviewProvider::class) recentTransactionUiState: RecentTransactionState ) { MifosMobileTheme { RecentTransactionScreen( diff --git a/feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/utils/RecentTransactionState.kt b/feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/utils/RecentTransactionState.kt new file mode 100644 index 0000000000..b09c51cd10 --- /dev/null +++ b/feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/utils/RecentTransactionState.kt @@ -0,0 +1,9 @@ +package org.mifos.mobile.feature.recent_transaction.utils + +import org.mifos.mobile.core.model.entity.Transaction + +sealed class RecentTransactionState { + data object Loading : RecentTransactionState() + data class Error(val message: String?) : RecentTransactionState() + data class Success(val transactions: List, val canPaginate: Boolean) : RecentTransactionState() +} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/recent_transactions/RecentTransactionViewModel.kt b/feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/viewmodel/RecentTransactionViewModel.kt similarity index 73% rename from app/src/main/java/org/mifos/mobile/ui/recent_transactions/RecentTransactionViewModel.kt rename to feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/viewmodel/RecentTransactionViewModel.kt index ddfb72716d..24fe4ce6e2 100644 --- a/app/src/main/java/org/mifos/mobile/ui/recent_transactions/RecentTransactionViewModel.kt +++ b/feature/recent_transaction/src/main/java/org/mifos/mobile/feature/recent_transaction/viewmodel/RecentTransactionViewModel.kt @@ -1,6 +1,5 @@ -package org.mifos.mobile.ui.recent_transactions +package org.mifos.mobile.feature.recent_transaction.viewmodel -import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -11,6 +10,7 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch import org.mifos.mobile.core.data.repositories.RecentTransactionRepository import org.mifos.mobile.core.model.entity.Transaction +import org.mifos.mobile.feature.recent_transaction.utils.RecentTransactionState import javax.inject.Inject @HiltViewModel @@ -20,8 +20,9 @@ class RecentTransactionViewModel @Inject constructor(private val recentTransacti private val limit = 50 private var transactions: MutableList = mutableListOf() - private val _recentTransactionUiState = MutableStateFlow(RecentTransactionUiState.Loading) - val recentTransactionUiState: StateFlow = _recentTransactionUiState + private val _recentTransactionUiState = MutableStateFlow(RecentTransactionState.Loading) + + val recentTransactionUiState: StateFlow = _recentTransactionUiState private val _isRefreshing = MutableStateFlow(false) val isRefreshing: StateFlow get() = _isRefreshing.asStateFlow() @@ -41,7 +42,7 @@ class RecentTransactionViewModel @Inject constructor(private val recentTransacti } fun loadInitialTransactions() { - _recentTransactionUiState.value = RecentTransactionUiState.Loading + _recentTransactionUiState.value = RecentTransactionState.Loading loadRecentTransactions(0) } @@ -49,11 +50,14 @@ class RecentTransactionViewModel @Inject constructor(private val recentTransacti viewModelScope.launch { recentTransactionRepositoryImp.recentTransactions(offset, limit) .catch { - _recentTransactionUiState.value = RecentTransactionUiState.Error(it.message) + _recentTransactionUiState.value = RecentTransactionState.Error(it.message) } .collect { transactions.plus(it.pageItems) - _recentTransactionUiState.value = RecentTransactionUiState.Success(transactions = transactions, canPaginate = it.pageItems.isNotEmpty()) + _recentTransactionUiState.value = RecentTransactionState.Success( + transactions = transactions, + canPaginate = it.pageItems.isNotEmpty() + ) _isPaginating.emit(false) _isRefreshing.emit(false) } @@ -61,9 +65,3 @@ class RecentTransactionViewModel @Inject constructor(private val recentTransacti } } - -sealed class RecentTransactionUiState { - data object Loading : RecentTransactionUiState() - data class Error(val message: String?) : RecentTransactionUiState() - data class Success(val transactions: List, val canPaginate: Boolean) : RecentTransactionUiState() -} diff --git a/feature/recent_transaction/src/main/res/drawable/ic_error_black_24dp.xml b/feature/recent_transaction/src/main/res/drawable/ic_error_black_24dp.xml new file mode 100644 index 0000000000..b666c18189 --- /dev/null +++ b/feature/recent_transaction/src/main/res/drawable/ic_error_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/recent_transaction/src/main/res/drawable/ic_local_atm_black_24dp.xml b/feature/recent_transaction/src/main/res/drawable/ic_local_atm_black_24dp.xml new file mode 100644 index 0000000000..07b27418c7 --- /dev/null +++ b/feature/recent_transaction/src/main/res/drawable/ic_local_atm_black_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/feature/recent_transaction/src/main/res/values/strings.xml b/feature/recent_transaction/src/main/res/values/strings.xml new file mode 100644 index 0000000000..04fd047d3f --- /dev/null +++ b/feature/recent_transaction/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + + + Recent Transactions + No Transaction + ATM Icon + %1$s %2$s + \ No newline at end of file diff --git a/feature/recent_transaction/src/test/java/org/mifos/mobile/feature/recent_transaction/ExampleUnitTest.kt b/feature/recent_transaction/src/test/java/org/mifos/mobile/feature/recent_transaction/ExampleUnitTest.kt new file mode 100644 index 0000000000..5a98633d8a --- /dev/null +++ b/feature/recent_transaction/src/test/java/org/mifos/mobile/feature/recent_transaction/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.recent_transaction + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/registration/build.gradle.kts b/feature/registration/build.gradle.kts index e0d853980e..060fd034ea 100644 --- a/feature/registration/build.gradle.kts +++ b/feature/registration/build.gradle.kts @@ -1,6 +1,4 @@ plugins { - alias(libs.plugins.android.library) - alias(libs.plugins.kotlin.android) alias(libs.plugins.mifos.android.feature) alias(libs.plugins.mifos.android.library.compose) } diff --git a/feature/registration/src/main/java/org/mifos/mobile/feature/registration/screens/RegistrationScreen.kt b/feature/registration/src/main/java/org/mifos/mobile/feature/registration/screens/RegistrationScreen.kt index daa892202d..5974ff4e9c 100644 --- a/feature/registration/src/main/java/org/mifos/mobile/feature/registration/screens/RegistrationScreen.kt +++ b/feature/registration/src/main/java/org/mifos/mobile/feature/registration/screens/RegistrationScreen.kt @@ -1,9 +1,12 @@ package org.mifos.mobile.feature.registration.screens +import android.app.Activity import android.content.Context import android.content.res.Configuration +import android.view.WindowInsets import android.widget.Toast import androidx.compose.foundation.border +import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement @@ -11,8 +14,10 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues 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.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape @@ -32,12 +37,15 @@ import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect 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.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -45,6 +53,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation @@ -57,6 +66,7 @@ import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat.getString import androidx.core.util.PatternsCompat +import androidx.core.view.WindowCompat import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.owlbuddy.www.countrycodechooser.CountryCodeChooser @@ -107,7 +117,7 @@ fun RegistrationScreen( ) } -@OptIn(ExperimentalComposeUiApi::class) + @Composable fun RegistrationScreen( uiState: RegistrationState, @@ -124,6 +134,7 @@ fun RegistrationScreen( MFScaffold( topBarTitleResId = R.string.register, navigateBack = navigateBack, + snackbarHost = { SnackbarHost(hostState = snackBarHostState) }, scaffoldContent = { contentPadding -> Box( @@ -155,7 +166,7 @@ fun RegistrationScreen( } } } - }, snackbarHost = { SnackbarHost(hostState = snackBarHostState) } + } ) } @@ -166,7 +177,6 @@ fun RegistrationContent( snackBarHostState: SnackbarHostState, ) { val context = LocalContext.current - val keyboardController = LocalSoftwareKeyboardController.current var accountNumber by rememberSaveable(stateSaver = TextFieldValue.Saver) { @@ -199,8 +209,8 @@ fun RegistrationContent( var countryCode by rememberSaveable { mutableStateOf("") } - val radioOptions = - listOf(stringResource(id = R.string.rb_email), stringResource(id = R.string.rb_mobile)) + val radioOptions = listOf(stringResource(id = R.string.rb_email), stringResource(id = R.string.rb_mobile)) + var authenticationMode by remember { mutableStateOf(radioOptions[0]) } val progressIndicator = progress(password.text) @@ -209,6 +219,10 @@ fun RegistrationContent( val scrollState = rememberScrollState() val coroutineScope = rememberCoroutineScope() + LaunchedEffect(scrollState.canScrollForward){ + if (scrollState.canScrollForward) scrollState.scrollTo(scrollState.maxValue) + } + Column( modifier = Modifier .fillMaxSize() @@ -221,7 +235,8 @@ fun RegistrationContent( .verticalScroll( state = scrollState, enabled = true - )) { + ) + ) { MifosMobileIcon(id = R.drawable.mifos_logo) @@ -405,6 +420,8 @@ fun RegistrationContent( ) { Text(text = stringResource(id = R.string.register)) } + + Spacer(modifier = Modifier.imePadding()) } } @@ -557,7 +574,6 @@ fun updatePasswordStrengthView(password: String, context: Context): Float { } class RegistrationScreenPreviewProvider : PreviewParameterProvider { - override val values: Sequence get() = sequenceOf( RegistrationState.Loading, @@ -567,9 +583,8 @@ class RegistrationScreenPreviewProvider : PreviewParameterProvider Unit?, diff --git a/feature/savings/.gitignore b/feature/savings/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/savings/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/savings/build.gradle.kts b/feature/savings/build.gradle.kts new file mode 100644 index 0000000000..685d49fff3 --- /dev/null +++ b/feature/savings/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) + alias(libs.plugins.kotlin.parcelize) +} + +android { + namespace = "org.mifos.mobile.feature.savings" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/savings/consumer-rules.pro b/feature/savings/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/savings/proguard-rules.pro b/feature/savings/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/savings/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/savings/src/androidTest/java/org/mifos/mobile/feature/savings/ExampleInstrumentedTest.kt b/feature/savings/src/androidTest/java/org/mifos/mobile/feature/savings/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..1340d6e8b6 --- /dev/null +++ b/feature/savings/src/androidTest/java/org/mifos/mobile/feature/savings/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.savings + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.savings.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/savings/src/main/AndroidManifest.xml b/feature/savings/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/savings/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsDetailViewModel.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingAccountsDetailViewModel.kt similarity index 96% rename from app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsDetailViewModel.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingAccountsDetailViewModel.kt index 5a127ac1d8..56b0490fa8 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingAccountsDetailViewModel.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingAccountsDetailViewModel.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account +package org.mifos.mobile.feature.savings.savings_account import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf @@ -8,7 +8,6 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch -import org.mifos.mobile.R import org.mifos.mobile.core.ui.theme.Blue import org.mifos.mobile.core.ui.theme.DepositGreen import org.mifos.mobile.core.ui.theme.LightYellow @@ -16,6 +15,7 @@ import org.mifos.mobile.core.ui.theme.RedLight import org.mifos.mobile.core.data.repositories.SavingsAccountRepository import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations import org.mifos.mobile.core.model.entity.accounts.savings.Status +import org.mifos.mobile.feature.savings.R import javax.inject.Inject @HiltViewModel diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountDetailContent.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingsAccountDetailContent.kt similarity index 98% rename from app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountDetailContent.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingsAccountDetailContent.kt index 707dba221d..b0caeef309 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountDetailContent.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingsAccountDetailContent.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account +package org.mifos.mobile.feature.savings.savings_account import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -27,7 +27,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import org.mifos.mobile.R import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations import org.mifos.mobile.core.model.entity.accounts.savings.Status import org.mifos.mobile.core.ui.component.MifosLinkText @@ -35,7 +34,8 @@ import org.mifos.mobile.core.ui.component.MifosTextTitleDescDoubleLine import org.mifos.mobile.core.ui.component.MonitorListItemWithIcon import org.mifos.mobile.core.common.utils.CurrencyUtil import org.mifos.mobile.core.common.utils.DateHelper -import org.mifos.mobile.utils.SymbolsUtils +import org.mifos.mobile.core.common.utils.SymbolsUtils +import org.mifos.mobile.feature.savings.R @Composable fun SavingsAccountDetailContent( @@ -227,7 +227,7 @@ fun LastTransactionCard( OutlinedCard(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.padding(14.dp)) { MifosTextTitleDescDoubleLine( - title = stringResource(id = R.string.last_transaction), + title = stringResource(id = R.string.last_trans), descriptionStyle = MaterialTheme.typography.bodyLarge, description = if (isTransactionEmpty) { stringResource(id = R.string.no_transaction) diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountDetailScreen.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingsAccountDetailScreen.kt similarity index 95% rename from app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountDetailScreen.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingsAccountDetailScreen.kt index 0ab5c945fe..0c780de757 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountDetailScreen.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingsAccountDetailScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account +package org.mifos.mobile.feature.savings.savings_account import android.widget.Toast import androidx.compose.foundation.layout.Column @@ -8,13 +8,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.EmptyDataView import org.mifos.mobile.core.ui.component.MifosProgressIndicator import org.mifos.mobile.core.ui.component.NoInternet import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.common.Network import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations +import org.mifos.mobile.feature.savings.R @Composable fun SavingsAccountDetailScreen( @@ -83,7 +83,6 @@ fun ErrorComponent( val context = LocalContext.current if (!Network.isConnected(context)) { NoInternet( - icon = R.drawable.ic_portable_wifi_off_black_24dp, error = R.string.no_internet_connection, isRetryEnabled = true, retry = retryConnection @@ -93,7 +92,6 @@ fun ErrorComponent( ).show() } else { EmptyDataView( - icon = R.drawable.ic_error_black_24dp, error = R.string.error_saving_account_details_loading ) Toast.makeText( diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountDetailTopBar.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingsAccountDetailTopBar.kt similarity index 96% rename from app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountDetailTopBar.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingsAccountDetailTopBar.kt index 47d3ba87d6..18b6f1c6ed 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account/SavingsAccountDetailTopBar.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account/SavingsAccountDetailTopBar.kt @@ -1,6 +1,5 @@ -package org.mifos.mobile.ui.savings_account +package org.mifos.mobile.feature.savings.savings_account -import androidx.compose.foundation.background import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.MoreVert @@ -21,8 +20,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import org.mifos.mobile.R import org.mifos.mobile.core.ui.theme.MifosMobileTheme +import org.mifos.mobile.feature.savings.R @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationContent.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_application/SavingsAccountApplicationContent.kt similarity index 98% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationContent.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_application/SavingsAccountApplicationContent.kt index cddd90f43c..b77a8b79bc 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationContent.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_application/SavingsAccountApplicationContent.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_application +package org.mifos.mobile.feature.savings.savings_account_application import android.content.Context import android.widget.Toast @@ -37,10 +37,10 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import org.mifos.mobile.R import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTemplate import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.common.utils.getTodayFormatted +import org.mifos.mobile.feature.savings.R @Composable fun SavingsAccountApplicationContent( diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationScreen.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_application/SavingsAccountApplicationScreen.kt similarity index 95% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationScreen.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_application/SavingsAccountApplicationScreen.kt index 88d4f885bf..846915aaf1 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationScreen.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_application/SavingsAccountApplicationScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_application +package org.mifos.mobile.feature.savings.savings_account_application import android.widget.Toast import androidx.compose.foundation.layout.Column @@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.EmptyDataView import org.mifos.mobile.core.ui.component.MifosProgressIndicator import org.mifos.mobile.core.ui.component.MifosTopBar @@ -22,6 +21,7 @@ import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.common.Network import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations import org.mifos.mobile.core.model.enums.SavingsAccountState +import org.mifos.mobile.feature.savings.R @Composable @@ -83,7 +83,6 @@ fun ErrorComponent(retryConnection: () -> Unit, errorMessage: String?) { val context = LocalContext.current if (!Network.isConnected(context)) { NoInternet( - icon = R.drawable.ic_portable_wifi_off_black_24dp, error = R.string.no_internet_connection, isRetryEnabled = true, retry = retryConnection @@ -92,7 +91,6 @@ fun ErrorComponent(retryConnection: () -> Unit, errorMessage: String?) { } else { EmptyDataView( modifier= Modifier.fillMaxSize(), - icon = R.drawable.ic_error_black_24dp, error = R.string.error_saving_account_details_loading, errorString = errorMessage ) diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationViewModel.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_application/SavingsAccountApplicationViewModel.kt similarity index 98% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationViewModel.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_application/SavingsAccountApplicationViewModel.kt index 16bd2c2e2e..1a98e03b96 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_application/SavingsAccountApplicationViewModel.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_application/SavingsAccountApplicationViewModel.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_application +package org.mifos.mobile.feature.savings.savings_account_application import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf @@ -7,7 +7,6 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch -import org.mifos.mobile.R import org.mifos.mobile.core.data.repositories.SavingsAccountRepository import org.mifos.mobile.core.datastore.PreferencesHelper import org.mifos.mobile.core.model.entity.accounts.savings.SavingsAccountApplicationPayload @@ -17,6 +16,7 @@ import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTempla import org.mifos.mobile.core.model.enums.SavingsAccountState import org.mifos.mobile.core.common.utils.DateHelper import org.mifos.mobile.core.common.utils.getTodayFormatted +import org.mifos.mobile.feature.savings.R import javax.inject.Inject @HiltViewModel diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountTransactionContent.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountTransactionContent.kt similarity index 98% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountTransactionContent.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountTransactionContent.kt index a5316f33e1..fd2e299461 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountTransactionContent.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountTransactionContent.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_transaction +package org.mifos.mobile.feature.savings.savings_account_transaction import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement @@ -25,11 +25,11 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import org.mifos.mobile.R import org.mifos.mobile.core.model.entity.accounts.savings.Transactions import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.common.utils.CurrencyUtil import org.mifos.mobile.core.common.utils.DateHelper +import org.mifos.mobile.feature.savings.R @Composable fun SavingsAccountTransactionContent( diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFilterDialog.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountsTransactionFilterDialog.kt similarity index 99% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFilterDialog.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountsTransactionFilterDialog.kt index 685f49cffe..d796c17cac 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionFilterDialog.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountsTransactionFilterDialog.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_transaction +package org.mifos.mobile.feature.savings.savings_account_transaction import androidx.compose.foundation.gestures.Orientation @@ -29,7 +29,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.MifosCheckBox import org.mifos.mobile.core.ui.component.MifosIconTextButton import org.mifos.mobile.core.ui.component.MifosIcons @@ -37,6 +36,7 @@ import org.mifos.mobile.core.ui.component.MifosRadioButton import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.common.utils.DateHelper import org.mifos.mobile.core.common.utils.DateHelper.getDateAsStringFromLong +import org.mifos.mobile.feature.savings.R import java.time.Instant @Composable @@ -275,6 +275,4 @@ fun SavingsTransactionFilterDialogPreview() { onDismiss = {}, ) } -} - - +} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionScreen.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountsTransactionScreen.kt similarity index 98% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionScreen.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountsTransactionScreen.kt index a0b137f136..ad98080ecb 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionScreen.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountsTransactionScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_transaction +package org.mifos.mobile.feature.savings.savings_account_transaction import androidx.compose.foundation.layout.Box @@ -21,7 +21,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.EmptyDataView import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosErrorComponent @@ -31,6 +30,7 @@ import org.mifos.mobile.core.ui.component.MifosTopBar import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.common.Network import org.mifos.mobile.core.model.entity.accounts.savings.Transactions +import org.mifos.mobile.feature.savings.R import java.time.Instant @Composable @@ -157,5 +157,4 @@ fun SavingsAccountTransactionScreenPreview( filterList = { } ) } -} - +} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionViewModel.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountsTransactionViewModel.kt similarity index 94% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionViewModel.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountsTransactionViewModel.kt index fdfb02fecc..249cfae877 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_transaction/SavingAccountsTransactionViewModel.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_transaction/SavingAccountsTransactionViewModel.kt @@ -1,5 +1,4 @@ -package org.mifos.mobile.ui.savings_account_transaction - +package org.mifos.mobile.feature.savings.savings_account_transaction import android.os.Parcelable import androidx.compose.ui.graphics.Color @@ -11,7 +10,6 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize -import org.mifos.mobile.R import org.mifos.mobile.core.ui.theme.DepositGreen import org.mifos.mobile.core.ui.theme.GreenSuccess import org.mifos.mobile.core.ui.theme.RedLight @@ -19,13 +17,16 @@ import org.mifos.mobile.core.data.repositories.SavingsAccountRepository import org.mifos.mobile.core.model.entity.accounts.savings.TransactionType import org.mifos.mobile.core.model.entity.accounts.savings.Transactions import org.mifos.mobile.core.common.utils.DateHelper +import org.mifos.mobile.feature.savings.R import javax.inject.Inject @HiltViewModel class SavingAccountsTransactionViewModel @Inject constructor(private val savingsAccountRepositoryImp: SavingsAccountRepository) : ViewModel() { - private val _savingAccountsTransactionUiState = MutableStateFlow(SavingsAccountTransactionUiState.Loading) + private val _savingAccountsTransactionUiState = MutableStateFlow( + SavingsAccountTransactionUiState.Loading + ) val savingAccountsTransactionUiState: StateFlow get() = _savingAccountsTransactionUiState private var _transactionsList: List = mutableListOf() @@ -77,7 +78,8 @@ class SavingAccountsTransactionViewModel @Inject constructor(private val savings ) } else -> { - _savingAccountsTransactionUiState.value = SavingsAccountTransactionUiState.Success(transactionsList) + _savingAccountsTransactionUiState.value = + SavingsAccountTransactionUiState.Success(transactionsList) } } } @@ -90,7 +92,8 @@ class SavingAccountsTransactionViewModel @Inject constructor(private val savings ) { val typeFilteredList = filterSavingsAccountTransactionsByType(checkBoxFilters = checkBoxFilters) val dateAndTypeFilteredList = filterTransactionListByDate(transactions = typeFilteredList, startDate = startDate, endDate = endDate) - _savingAccountsTransactionUiState.value = SavingsAccountTransactionUiState.Success(dateAndTypeFilteredList) + _savingAccountsTransactionUiState.value = + SavingsAccountTransactionUiState.Success(dateAndTypeFilteredList) } private fun filterByDate(startDate: Long, endDate: Long) { @@ -199,5 +202,4 @@ enum class SavingsTransactionCheckBoxFilter( textResId = R.string.interest_posting, checkBoxColor = GreenSuccess ) -} - +} \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_withdraw/SavingsAccountWithdrawScreen.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_withdraw/SavingsAccountWithdrawScreen.kt similarity index 98% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_withdraw/SavingsAccountWithdrawScreen.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_withdraw/SavingsAccountWithdrawScreen.kt index 95230fcce3..16625947cb 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_withdraw/SavingsAccountWithdrawScreen.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_withdraw/SavingsAccountWithdrawScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_withdraw +package org.mifos.mobile.feature.savings.savings_account_withdraw import android.widget.Toast import androidx.compose.foundation.background @@ -28,7 +28,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.MifosOutlinedTextField import org.mifos.mobile.core.ui.component.MifosProgressIndicator import org.mifos.mobile.core.ui.component.MifosTitleDescSingleLineEqual @@ -38,6 +37,7 @@ import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.common.Network import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations import org.mifos.mobile.core.common.utils.getTodayFormatted +import org.mifos.mobile.feature.savings.R @Composable @@ -106,7 +106,6 @@ fun ErrorComponent( val context = LocalContext.current if (!Network.isConnected(context)) { NoInternet( - icon = R.drawable.ic_portable_wifi_off_black_24dp, error = R.string.no_internet_connection, isRetryEnabled = false, ) diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_account_withdraw/SavingsAccountWithdrawViewModel.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_withdraw/SavingsAccountWithdrawViewModel.kt similarity index 93% rename from app/src/main/java/org/mifos/mobile/ui/savings_account_withdraw/SavingsAccountWithdrawViewModel.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_withdraw/SavingsAccountWithdrawViewModel.kt index a7e16928d6..ca168116a5 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_account_withdraw/SavingsAccountWithdrawViewModel.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_account_withdraw/SavingsAccountWithdrawViewModel.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_account_withdraw +package org.mifos.mobile.feature.savings.savings_account_withdraw import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -18,7 +18,9 @@ class SavingsAccountWithdrawViewModel @Inject constructor( private val savingsAccountRepositoryImp: SavingsAccountRepository ) : ViewModel() { val savingsAccountWithdrawUiState: StateFlow get() = _savingsAccountWithdrawUiState - private val _savingsAccountWithdrawUiState = MutableStateFlow(SavingsAccountWithdrawUiState.WithdrawUiReady) + private val _savingsAccountWithdrawUiState = MutableStateFlow( + SavingsAccountWithdrawUiState.WithdrawUiReady + ) val savingsWithAssociations: StateFlow get() = _savingsWithAssociations private var _savingsWithAssociations: MutableStateFlow = MutableStateFlow(null) diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferContent.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_make_transfer/SavingsMakeTransferContent.kt similarity index 96% rename from app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferContent.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_make_transfer/SavingsMakeTransferContent.kt index 4df86fe2c0..aef4fe3f28 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferContent.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_make_transfer/SavingsMakeTransferContent.kt @@ -1,9 +1,5 @@ -package org.mifos.mobile.ui.savings_make_transfer +package org.mifos.mobile.feature.savings.savings_make_transfer -import android.util.Log -import android.widget.Toast -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.scrollable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -35,12 +31,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp -import org.mifos.mobile.R import org.mifos.mobile.core.model.entity.templates.account.AccountOption import org.mifos.mobile.core.ui.component.MFStepProcess import org.mifos.mobile.core.ui.component.MifosDropDownDoubleTextField @@ -50,6 +42,7 @@ import org.mifos.mobile.core.ui.component.getStepState import org.mifos.mobile.core.ui.theme.DarkGray import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.ui.theme.Primary +import org.mifos.mobile.feature.savings.R @Composable fun SavingsMakeTransferContent( diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferScreen.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_make_transfer/SavingsMakeTransferScreen.kt similarity index 97% rename from app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferScreen.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_make_transfer/SavingsMakeTransferScreen.kt index 280eb62710..d236402ebd 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferScreen.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_make_transfer/SavingsMakeTransferScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_make_transfer +package org.mifos.mobile.feature.savings.savings_make_transfer import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -11,12 +11,12 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosErrorComponent import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.common.Network +import org.mifos.mobile.feature.savings.R @Composable fun SavingsMakeTransferScreen( diff --git a/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferViewModel.kt b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_make_transfer/SavingsMakeTransferViewModel.kt similarity index 98% rename from app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferViewModel.kt rename to feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_make_transfer/SavingsMakeTransferViewModel.kt index 919aee6556..d95542c37c 100644 --- a/app/src/main/java/org/mifos/mobile/ui/savings_make_transfer/SavingsMakeTransferViewModel.kt +++ b/feature/savings/src/main/java/org/mifos/mobile/feature/savings/savings_make_transfer/SavingsMakeTransferViewModel.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.savings_make_transfer +package org.mifos.mobile.feature.savings.savings_make_transfer import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope diff --git a/feature/savings/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml b/feature/savings/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml new file mode 100644 index 0000000000..7aeaab0975 --- /dev/null +++ b/feature/savings/src/main/res/drawable/ic_assignment_turned_in_black_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/feature/savings/src/main/res/drawable/ic_charges.xml b/feature/savings/src/main/res/drawable/ic_charges.xml new file mode 100644 index 0000000000..dc27ed8afe --- /dev/null +++ b/feature/savings/src/main/res/drawable/ic_charges.xml @@ -0,0 +1,46 @@ + + + + + + + + + + diff --git a/feature/savings/src/main/res/drawable/ic_compare_arrows_black_24dp.xml b/feature/savings/src/main/res/drawable/ic_compare_arrows_black_24dp.xml new file mode 100644 index 0000000000..a16bbb7847 --- /dev/null +++ b/feature/savings/src/main/res/drawable/ic_compare_arrows_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/savings/src/main/res/drawable/ic_qrcode_scan.xml b/feature/savings/src/main/res/drawable/ic_qrcode_scan.xml new file mode 100644 index 0000000000..5b0e12ba00 --- /dev/null +++ b/feature/savings/src/main/res/drawable/ic_qrcode_scan.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/savings/src/main/res/drawable/triangular_green_view.xml b/feature/savings/src/main/res/drawable/triangular_green_view.xml new file mode 100644 index 0000000000..3644ed67c3 --- /dev/null +++ b/feature/savings/src/main/res/drawable/triangular_green_view.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/feature/savings/src/main/res/drawable/triangular_red_view.xml b/feature/savings/src/main/res/drawable/triangular_red_view.xml new file mode 100644 index 0000000000..66994c39a5 --- /dev/null +++ b/feature/savings/src/main/res/drawable/triangular_red_view.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/feature/savings/src/main/res/values/strings.xml b/feature/savings/src/main/res/values/strings.xml new file mode 100644 index 0000000000..1c73ba2690 --- /dev/null +++ b/feature/savings/src/main/res/values/strings.xml @@ -0,0 +1,88 @@ + + + Active + Need Approval + Pending + Matured + Closed + Contact Us + 8000000000 + Nominal Interest Rate + Account Number + Account Balance + Total Deposits + %1$.2f %2$s + %1$s %2$s + Account Status + NA + Total Withdrawals + No withdrawals + Make Transfer + Deposit + Made on + No Transaction + %1$s %2$.2f + Last Transaction + Min. Required Bal. + Monitor + Transactions + View Transactions + Savings Charges + View Charges + QR Code + Share QR Code + View QR Code for this account + Approval Pending + No Internet Connection + Please make sure you are connected to internet + Error loading in saving accounts list + Error loading in saving accounts detail + + Update Savings Account + Withdraw Savings Account + Saving Account Details + Client Name + Submission Date + Submit + Select Product Id + Apply Savings Account + New Saving Account created successfully. + Saving Account Updated Successfully + Select all filters you want to apply + Filter Savings Accounts + Clear Filters + Cancel + Filter + Savings Account Transaction + No Transaction Found + Date + 4 Weeks + 3 Months + 6 Months + Dividend Payout + Withdrawal + Interest Posting + Savings Account Withdraw Successful + Withdrawal Date + Remark + %1$s cannot be blank + 1 + 2 + 3 + 4 + Enter Amount + Amount should be greater than zero + Pay To + Pay From + Select Account to Pay To + Select Account to Pay From + Required + Continue + Invalid Amount + Amount + Review + Enter Remarks for transfer + Transfer + + + \ No newline at end of file diff --git a/feature/savings/src/test/java/org/mifos/mobile/feature/savings/ExampleUnitTest.kt b/feature/savings/src/test/java/org/mifos/mobile/feature/savings/ExampleUnitTest.kt new file mode 100644 index 0000000000..47d3bf4c49 --- /dev/null +++ b/feature/savings/src/test/java/org/mifos/mobile/feature/savings/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.savings + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/settings/.gitignore b/feature/settings/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/settings/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts new file mode 100644 index 0000000000..954e6e86fd --- /dev/null +++ b/feature/settings/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + +android { + namespace = "org.mifos.mobile.feature.settings" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + implementation(libs.androidx.appcompat) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/settings/consumer-rules.pro b/feature/settings/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/settings/proguard-rules.pro b/feature/settings/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/settings/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/settings/src/androidTest/java/org/mifos/mobile/feature/settings/ExampleInstrumentedTest.kt b/feature/settings/src/androidTest/java/org/mifos/mobile/feature/settings/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..dfe1d4a46c --- /dev/null +++ b/feature/settings/src/androidTest/java/org/mifos/mobile/feature/settings/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.settings + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.settings.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/settings/src/main/AndroidManifest.xml b/feature/settings/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/settings/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/settings/SettingsScreen.kt b/feature/settings/src/main/java/org/mifos/mobile/feature/settings/SettingsScreen.kt similarity index 99% rename from app/src/main/java/org/mifos/mobile/ui/settings/SettingsScreen.kt rename to feature/settings/src/main/java/org/mifos/mobile/feature/settings/SettingsScreen.kt index cc92ed3baa..c82dc22cdd 100644 --- a/app/src/main/java/org/mifos/mobile/ui/settings/SettingsScreen.kt +++ b/feature/settings/src/main/java/org/mifos/mobile/feature/settings/SettingsScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.settings +package org.mifos.mobile.feature.settings import android.content.Context import androidx.compose.foundation.layout.Column @@ -33,7 +33,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosRadioButtonDialog import org.mifos.mobile.core.ui.component.MifosTopBarTitle diff --git a/app/src/main/java/org/mifos/mobile/ui/settings/SettingsViewModel.kt b/feature/settings/src/main/java/org/mifos/mobile/feature/settings/SettingsViewModel.kt similarity index 79% rename from app/src/main/java/org/mifos/mobile/ui/settings/SettingsViewModel.kt rename to feature/settings/src/main/java/org/mifos/mobile/feature/settings/SettingsViewModel.kt index fe03424ec8..1aeab75741 100644 --- a/app/src/main/java/org/mifos/mobile/ui/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/java/org/mifos/mobile/feature/settings/SettingsViewModel.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.settings +package org.mifos.mobile.feature.settings import android.os.Build @@ -11,7 +11,6 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.stateIn -import org.mifos.mobile.R import org.mifos.mobile.core.datastore.PreferencesHelper import org.mifos.mobile.core.model.enums.AppTheme import org.mifos.mobile.core.model.enums.MifosAppLanguage @@ -72,7 +71,7 @@ class SettingsViewModel @Inject constructor( } ) preferencesHelper.appTheme = theme.ordinal - preferencesHelper.applySavedTheme() + preferencesHelper.applyTheme(theme) } } @@ -119,4 +118,28 @@ enum class SettingsCardItem( ) } +fun PreferencesHelper.applySavedTheme() { + val applicationTheme = AppTheme.entries.find { it.ordinal == this.appTheme } + AppCompatDelegate.setDefaultNightMode( + when { + applicationTheme == AppTheme.DARK -> AppCompatDelegate.MODE_NIGHT_YES + applicationTheme == AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO + Build.VERSION.SDK_INT > Build.VERSION_CODES.P -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + else -> AppCompatDelegate.MODE_NIGHT_NO + }, + ) +} + + +fun PreferencesHelper.applyTheme(applicationTheme: AppTheme) { + this.appTheme = applicationTheme.ordinal + AppCompatDelegate.setDefaultNightMode( + when { + applicationTheme == AppTheme.DARK -> AppCompatDelegate.MODE_NIGHT_YES + applicationTheme == AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO + Build.VERSION.SDK_INT > Build.VERSION_CODES.P -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + else -> AppCompatDelegate.MODE_NIGHT_NO + }, + ) +} diff --git a/app/src/main/java/org/mifos/mobile/ui/settings/UpdateEndpointDialog.kt b/feature/settings/src/main/java/org/mifos/mobile/feature/settings/UpdateEndpointDialog.kt similarity index 98% rename from app/src/main/java/org/mifos/mobile/ui/settings/UpdateEndpointDialog.kt rename to feature/settings/src/main/java/org/mifos/mobile/feature/settings/UpdateEndpointDialog.kt index 82f82591da..ca01b712cc 100644 --- a/app/src/main/java/org/mifos/mobile/ui/settings/UpdateEndpointDialog.kt +++ b/feature/settings/src/main/java/org/mifos/mobile/feature/settings/UpdateEndpointDialog.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.settings +package org.mifos.mobile.feature.settings import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -21,7 +21,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog -import org.mifos.mobile.R import org.mifos.mobile.core.ui.theme.MifosMobileTheme @Composable diff --git a/feature/settings/src/main/res/drawable/ic_baseline_dark_mode_24.xml b/feature/settings/src/main/res/drawable/ic_baseline_dark_mode_24.xml new file mode 100644 index 0000000000..e52c5bafff --- /dev/null +++ b/feature/settings/src/main/res/drawable/ic_baseline_dark_mode_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/feature/settings/src/main/res/drawable/ic_lock_black_24dp.xml b/feature/settings/src/main/res/drawable/ic_lock_black_24dp.xml new file mode 100644 index 0000000000..54a8520607 --- /dev/null +++ b/feature/settings/src/main/res/drawable/ic_lock_black_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_passcode.xml b/feature/settings/src/main/res/drawable/ic_passcode.xml similarity index 98% rename from app/src/main/res/drawable/ic_passcode.xml rename to feature/settings/src/main/res/drawable/ic_passcode.xml index d005097405..bc2ba28ef6 100644 --- a/app/src/main/res/drawable/ic_passcode.xml +++ b/feature/settings/src/main/res/drawable/ic_passcode.xml @@ -1,11 +1,11 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_translate.xml b/feature/settings/src/main/res/drawable/ic_translate.xml similarity index 98% rename from app/src/main/res/drawable/ic_translate.xml rename to feature/settings/src/main/res/drawable/ic_translate.xml index ec8633e202..fbabff144e 100644 --- a/app/src/main/res/drawable/ic_translate.xml +++ b/feature/settings/src/main/res/drawable/ic_translate.xml @@ -1,11 +1,11 @@ - - - + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_update.xml b/feature/settings/src/main/res/drawable/ic_update.xml similarity index 98% rename from app/src/main/res/drawable/ic_update.xml rename to feature/settings/src/main/res/drawable/ic_update.xml index ab86a8ce5f..bb668bb319 100644 --- a/app/src/main/res/drawable/ic_update.xml +++ b/feature/settings/src/main/res/drawable/ic_update.xml @@ -1,11 +1,11 @@ - - - + + + \ No newline at end of file diff --git a/feature/settings/src/main/res/values/strings.xml b/feature/settings/src/main/res/values/strings.xml new file mode 100644 index 0000000000..aba0d20dd7 --- /dev/null +++ b/feature/settings/src/main/res/values/strings.xml @@ -0,0 +1,44 @@ + + + Change theme + Settings + Password + Change Passcode + Change App Passcode + Change Password + Change your Account Password + Language + Choose your language + Other + Accounts + Theme + Update Endpoint + Click here to change your Endpoint Configurations + Enter the Base URL + Enter the Tenant + Cancel + OK + + default_system_language + language_type + + System Language + English + हिंदी + عربى + اُردُو + বাঙালি + Español + français + bahasa Indonesia + ភាសាខ្មែរ + ಕನ್ನಡ + తెలుగు + မြန်မာ + Polski + Português + русский + Kiswahili + فارسی + + \ No newline at end of file diff --git a/feature/settings/src/test/java/org/mifos/mobile/feature/settings/ExampleUnitTest.kt b/feature/settings/src/test/java/org/mifos/mobile/feature/settings/ExampleUnitTest.kt new file mode 100644 index 0000000000..f4b977b8ac --- /dev/null +++ b/feature/settings/src/test/java/org/mifos/mobile/feature/settings/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.settings + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/third-party-transfer/.gitignore b/feature/third-party-transfer/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/third-party-transfer/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/third-party-transfer/build.gradle.kts b/feature/third-party-transfer/build.gradle.kts new file mode 100644 index 0000000000..4069e46c27 --- /dev/null +++ b/feature/third-party-transfer/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) + alias(libs.plugins.kotlin.parcelize) +} + + +android { + namespace = "org.mifos.mobile.feature.third.party.transfer" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/third-party-transfer/consumer-rules.pro b/feature/third-party-transfer/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/third-party-transfer/proguard-rules.pro b/feature/third-party-transfer/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/third-party-transfer/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/third-party-transfer/src/androidTest/java/org/mifos/mobile/feature/third/party/transfer/ExampleInstrumentedTest.kt b/feature/third-party-transfer/src/androidTest/java/org/mifos/mobile/feature/third/party/transfer/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..ed2b8e9903 --- /dev/null +++ b/feature/third-party-transfer/src/androidTest/java/org/mifos/mobile/feature/third/party/transfer/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.third.party.transfer + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.third.party.transfer.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/third-party-transfer/src/main/AndroidManifest.xml b/feature/third-party-transfer/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/third-party-transfer/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferContent.kt b/feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferContent.kt similarity index 99% rename from app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferContent.kt rename to feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferContent.kt index 5f59cec8a3..eb67266b1f 100644 --- a/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferContent.kt +++ b/feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferContent.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.third_party_transfer +package org.mifos.mobile.feature.third.party.transfer.third_party_transfer import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -32,7 +32,6 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import org.mifos.mobile.R import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary import org.mifos.mobile.core.model.entity.templates.account.AccountOption import org.mifos.mobile.core.ui.component.MFStepProcess @@ -45,6 +44,7 @@ import org.mifos.mobile.core.ui.component.getStepState import org.mifos.mobile.core.ui.theme.DarkGray import org.mifos.mobile.core.ui.theme.MifosMobileTheme import org.mifos.mobile.core.ui.theme.Primary +import org.mifos.mobile.feature.third.party.transfer.R @Composable fun ThirdPartyTransferContent( diff --git a/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferScreen.kt b/feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferScreen.kt similarity index 96% rename from app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferScreen.kt rename to feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferScreen.kt index 56a89c8451..3700382b7b 100644 --- a/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferScreen.kt +++ b/feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.third_party_transfer +package org.mifos.mobile.feature.third.party.transfer.third_party_transfer import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding @@ -12,12 +12,12 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R +import org.mifos.mobile.core.common.Network import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosErrorComponent import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay import org.mifos.mobile.core.ui.theme.MifosMobileTheme -import org.mifos.mobile.core.common.Network +import org.mifos.mobile.feature.third.party.transfer.R @Composable fun ThirdPartyTransferScreen( diff --git a/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferViewModel.kt b/feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferViewModel.kt similarity index 88% rename from app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferViewModel.kt rename to feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferViewModel.kt index 21ccbe6cd1..8b36ec7147 100644 --- a/app/src/main/java/org/mifos/mobile/ui/third_party_transfer/ThirdPartyTransferViewModel.kt +++ b/feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferViewModel.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.third_party_transfer +package org.mifos.mobile.feature.third.party.transfer.third_party_transfer import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -21,10 +21,14 @@ class ThirdPartyTransferViewModel @Inject constructor( private val beneficiaryRepositoryImp: BeneficiaryRepository ) : ViewModel() { - private val _thirdPartyTransferUiState = MutableStateFlow(ThirdPartyTransferUiState.Loading) + private val _thirdPartyTransferUiState = MutableStateFlow( + ThirdPartyTransferUiState.Loading + ) val thirdPartyTransferUiState: StateFlow get() = _thirdPartyTransferUiState - private val _thirdPartyTransferUiData = MutableStateFlow(ThirdPartyTransferUiData()) + private val _thirdPartyTransferUiData = MutableStateFlow( + ThirdPartyTransferUiData() + ) val thirdPartyTransferUiData: StateFlow get() = _thirdPartyTransferUiData init { @@ -45,7 +49,8 @@ class ThirdPartyTransferViewModel @Inject constructor( ) } }.catch { - _thirdPartyTransferUiState.value = ThirdPartyTransferUiState.Error(errorMessage = it.message) + _thirdPartyTransferUiState.value = + ThirdPartyTransferUiState.Error(errorMessage = it.message) }.collect { _thirdPartyTransferUiState.value = ThirdPartyTransferUiState.ShowUI } diff --git a/feature/third-party-transfer/src/main/res/values/strings.xml b/feature/third-party-transfer/src/main/res/values/strings.xml new file mode 100644 index 0000000000..043cd23685 --- /dev/null +++ b/feature/third-party-transfer/src/main/res/values/strings.xml @@ -0,0 +1,31 @@ + + + Third Party Transfer + Error to fetch Third Party Transfertemplate + 1 + 2 + 3 + 4 + Enter Amount + Amount should be greater than zero + Pay To + Pay From + Select Account to Pay To + Select Account to Pay From + Required + Continue + Invalid Amount + Amount + Review + Enter Remarks for transfer + Transfer + Remark is mandatory + Remark + Cancel + Select Beneficiary + Add Beneficiary + Beneficiary + Currently, you don\'t have any Beneficiary. Please add Beneficiary + Loan Type + + \ No newline at end of file diff --git a/feature/third-party-transfer/src/test/java/org/mifos/mobile/feature/third/party/transfer/ExampleUnitTest.kt b/feature/third-party-transfer/src/test/java/org/mifos/mobile/feature/third/party/transfer/ExampleUnitTest.kt new file mode 100644 index 0000000000..ce3a79bef5 --- /dev/null +++ b/feature/third-party-transfer/src/test/java/org/mifos/mobile/feature/third/party/transfer/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.third.party.transfer + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/transfer-process/.gitignore b/feature/transfer-process/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/transfer-process/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/transfer-process/build.gradle.kts b/feature/transfer-process/build.gradle.kts new file mode 100644 index 0000000000..889046adeb --- /dev/null +++ b/feature/transfer-process/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + + +android { + namespace = "org.mifos.mobile.feature.transfer.process" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/transfer-process/consumer-rules.pro b/feature/transfer-process/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/transfer-process/proguard-rules.pro b/feature/transfer-process/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/transfer-process/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/transfer-process/src/androidTest/java/org/mifos/mobile/feature/transfer/process/ExampleInstrumentedTest.kt b/feature/transfer-process/src/androidTest/java/org/mifos/mobile/feature/transfer/process/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..27b19e399a --- /dev/null +++ b/feature/transfer-process/src/androidTest/java/org/mifos/mobile/feature/transfer/process/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.transfer.process + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.transfer.process.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/transfer-process/src/main/AndroidManifest.xml b/feature/transfer-process/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/transfer-process/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessScreen.kt b/feature/transfer-process/src/main/java/org/mifos/mobile/feature/transfer/process/TransferProcessScreen.kt similarity index 99% rename from app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessScreen.kt rename to feature/transfer-process/src/main/java/org/mifos/mobile/feature/transfer/process/TransferProcessScreen.kt index 24cbad54bd..b22e1abe41 100644 --- a/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessScreen.kt +++ b/feature/transfer-process/src/main/java/org/mifos/mobile/feature/transfer/process/TransferProcessScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.transfer_process +package org.mifos.mobile.feature.transfer.process import android.widget.Toast import androidx.compose.foundation.BorderStroke @@ -34,7 +34,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.MFScaffold import org.mifos.mobile.core.ui.component.MifosErrorComponent import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay diff --git a/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessViewModel.kt b/feature/transfer-process/src/main/java/org/mifos/mobile/feature/transfer/process/TransferProcessViewModel.kt similarity index 98% rename from app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessViewModel.kt rename to feature/transfer-process/src/main/java/org/mifos/mobile/feature/transfer/process/TransferProcessViewModel.kt index 6b648e8b46..c50579e34a 100644 --- a/app/src/main/java/org/mifos/mobile/ui/transfer_process/TransferProcessViewModel.kt +++ b/feature/transfer-process/src/main/java/org/mifos/mobile/feature/transfer/process/TransferProcessViewModel.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.transfer_process +package org.mifos.mobile.feature.transfer.process import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope diff --git a/feature/transfer-process/src/main/res/values/strings.xml b/feature/transfer-process/src/main/res/values/strings.xml new file mode 100644 index 0000000000..2b5ca4856a --- /dev/null +++ b/feature/transfer-process/src/main/res/values/strings.xml @@ -0,0 +1,12 @@ + + + Transfer + Transferred Successfully + Amount + Transfer from Savings + Pay To + Pay From + Date + Cancel + Remark + \ No newline at end of file diff --git a/feature/transfer-process/src/test/java/org/mifos/mobile/feature/transfer/process/ExampleUnitTest.kt b/feature/transfer-process/src/test/java/org/mifos/mobile/feature/transfer/process/ExampleUnitTest.kt new file mode 100644 index 0000000000..e0bb4aa78e --- /dev/null +++ b/feature/transfer-process/src/test/java/org/mifos/mobile/feature/transfer/process/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.transfer.process + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/update-password/.gitignore b/feature/update-password/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/update-password/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/update-password/build.gradle.kts b/feature/update-password/build.gradle.kts new file mode 100644 index 0000000000..ed0d02665e --- /dev/null +++ b/feature/update-password/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) +} + + +android { + namespace = "org.mifos.mobile.feature.update.password" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/update-password/consumer-rules.pro b/feature/update-password/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/update-password/proguard-rules.pro b/feature/update-password/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/update-password/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/update-password/src/androidTest/java/org/mifos/mobile/feature/update_password/ExampleInstrumentedTest.kt b/feature/update-password/src/androidTest/java/org/mifos/mobile/feature/update_password/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..2894fdb41a --- /dev/null +++ b/feature/update-password/src/androidTest/java/org/mifos/mobile/feature/update_password/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.update_password + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.update_password.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/update-password/src/main/AndroidManifest.xml b/feature/update-password/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/update-password/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordContent.kt b/feature/update-password/src/main/java/org/mifos/mobile/feature/update_password/UpdatePasswordContent.kt similarity index 97% rename from app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordContent.kt rename to feature/update-password/src/main/java/org/mifos/mobile/feature/update_password/UpdatePasswordContent.kt index 654846ebf4..6aab473704 100644 --- a/app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordContent.kt +++ b/feature/update-password/src/main/java/org/mifos/mobile/feature/update_password/UpdatePasswordContent.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.update_password +package org.mifos.mobile.feature.update_password import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -22,7 +22,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -33,10 +32,9 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.MifosOutlinedTextField +import org.mifos.mobile.feature.update.password.R -@OptIn(ExperimentalComposeUiApi::class) @Composable fun UpdatePasswordContent( viewModel: UpdatePasswordViewModel = hiltViewModel(), diff --git a/app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordScreen.kt b/feature/update-password/src/main/java/org/mifos/mobile/feature/update_password/UpdatePasswordScreen.kt similarity index 88% rename from app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordScreen.kt rename to feature/update-password/src/main/java/org/mifos/mobile/feature/update_password/UpdatePasswordScreen.kt index 274ed281f4..c1a43831ba 100644 --- a/app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordScreen.kt +++ b/feature/update-password/src/main/java/org/mifos/mobile/feature/update_password/UpdatePasswordScreen.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.update_password +package org.mifos.mobile.feature.update_password import android.content.Context import android.widget.Toast @@ -23,15 +23,14 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import org.mifos.mobile.R import org.mifos.mobile.core.ui.component.MifosProgressIndicator import org.mifos.mobile.core.ui.component.MifosTopBar import org.mifos.mobile.core.ui.theme.MifosMobileTheme -import org.mifos.mobile.ui.savings_account_withdraw.UiStatesParameterProvider import org.mifos.mobile.core.common.Network -import org.mifos.mobile.feature.registration.utils.RegistrationState +import org.mifos.mobile.feature.update.password.R @Composable @@ -49,7 +48,7 @@ fun UpdatePasswordScreen( @Composable fun UpdatePasswordScreen( - uiState: RegistrationState, + uiState: UpdatePasswordUiState, navigateBack: () -> Unit ) { val context = LocalContext.current @@ -76,7 +75,7 @@ fun UpdatePasswordScreen( ) when (uiState) { - is RegistrationState.Loading -> { + is UpdatePasswordUiState.Loading -> { MifosProgressIndicator( modifier = Modifier .fillMaxSize() @@ -84,7 +83,7 @@ fun UpdatePasswordScreen( ) } - is RegistrationState.Error -> { + is UpdatePasswordUiState.Error -> { if (updatePasswordButtonClicked) { LaunchedEffect(snackbarHostState) { snackbarHostState.showSnackbar( @@ -96,9 +95,9 @@ fun UpdatePasswordScreen( } } - is RegistrationState.Initial -> Unit + is UpdatePasswordUiState.Initial -> Unit - is RegistrationState.Success -> { + is UpdatePasswordUiState.Success -> { LaunchedEffect(snackbarHostState) { snackbarHostState.showSnackbar( context.getString(R.string.password_changed_successfully), @@ -113,10 +112,20 @@ fun UpdatePasswordScreen( } } +class UiStatesParameterProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + UpdatePasswordUiState.Initial, + UpdatePasswordUiState.Error(1), + UpdatePasswordUiState.Loading, + UpdatePasswordUiState.Success + ) +} + @Composable @Preview(showSystemUi = true, showBackground = true) fun UpdatePasswordScreenPreview( - @PreviewParameter(UiStatesParameterProvider::class) registrationUiState: RegistrationState + @PreviewParameter(UiStatesParameterProvider::class) updatePasswordUiState: UpdatePasswordUiState ) { MifosMobileTheme { UpdatePasswordScreen(navigateBack = {}) diff --git a/app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordViewModel.kt b/feature/update-password/src/main/java/org/mifos/mobile/feature/update_password/UpdatePasswordViewModel.kt similarity index 56% rename from app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordViewModel.kt rename to feature/update-password/src/main/java/org/mifos/mobile/feature/update_password/UpdatePasswordViewModel.kt index bc2b72209b..94c7bd1e5c 100644 --- a/app/src/main/java/org/mifos/mobile/ui/update_password/UpdatePasswordViewModel.kt +++ b/feature/update-password/src/main/java/org/mifos/mobile/feature/update_password/UpdatePasswordViewModel.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.update_password +package org.mifos.mobile.feature.update_password import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -7,10 +7,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch -import org.mifos.mobile.R import org.mifos.mobile.core.data.repositories.ClientRepository import org.mifos.mobile.core.data.repositories.UserAuthRepository -import org.mifos.mobile.feature.registration.utils.RegistrationState +import org.mifos.mobile.feature.update.password.R import javax.inject.Inject @HiltViewModel @@ -19,19 +18,27 @@ class UpdatePasswordViewModel @Inject constructor( private val clientRepositoryImp: ClientRepository, ) : ViewModel() { - private val _updatePasswordUiState = MutableStateFlow(RegistrationState.Initial) - val updatePasswordUiState: StateFlow get() = _updatePasswordUiState + private val _updatePasswordUiState = MutableStateFlow(UpdatePasswordUiState.Initial) + val updatePasswordUiState: StateFlow get() = _updatePasswordUiState fun updateAccountPassword(newPassword: String, confirmPassword: String) { viewModelScope.launch { - _updatePasswordUiState.value = RegistrationState.Loading + _updatePasswordUiState.value = UpdatePasswordUiState.Loading userAuthRepositoryImp.updateAccountPassword(newPassword, confirmPassword).catch { _updatePasswordUiState.value = - RegistrationState.Error(R.string.could_not_update_password_error) + UpdatePasswordUiState.Error(R.string.could_not_update_password_error) }.collect { - _updatePasswordUiState.value = RegistrationState.Success + _updatePasswordUiState.value = UpdatePasswordUiState.Success clientRepositoryImp.updateAuthenticationToken(newPassword) } } } } + + +sealed class UpdatePasswordUiState { + data class Error(val exception: Int) : UpdatePasswordUiState() + data object Success : UpdatePasswordUiState() + data object Loading : UpdatePasswordUiState() + data object Initial: UpdatePasswordUiState() +} \ No newline at end of file diff --git a/feature/update-password/src/main/res/drawable/ic_lock_black_24dp.xml b/feature/update-password/src/main/res/drawable/ic_lock_black_24dp.xml new file mode 100644 index 0000000000..54a8520607 --- /dev/null +++ b/feature/update-password/src/main/res/drawable/ic_lock_black_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/feature/update-password/src/main/res/ic_lock_black_24dp.xml b/feature/update-password/src/main/res/ic_lock_black_24dp.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/update-password/src/main/res/values/strings.xml b/feature/update-password/src/main/res/values/strings.xml new file mode 100644 index 0000000000..dd0ff9e54f --- /dev/null +++ b/feature/update-password/src/main/res/values/strings.xml @@ -0,0 +1,18 @@ + + + We were unable to update password. + Change your Account Password + Current Password + New Password + %1$s changed successfully + Change Password + Confirm Password + %1$s cannot be blank + %1$s cannot be less than %2$d characters + OK + Password changed successfully + 6 + No Internet Connection + Password does not match. + + \ No newline at end of file diff --git a/feature/update-password/src/test/java/org/mifos/mobile/feature/update_password/ExampleUnitTest.kt b/feature/update-password/src/test/java/org/mifos/mobile/feature/update_password/ExampleUnitTest.kt new file mode 100644 index 0000000000..4eec58f73b --- /dev/null +++ b/feature/update-password/src/test/java/org/mifos/mobile/feature/update_password/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.update_password + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/user_profile/.gitignore b/feature/user_profile/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/feature/user_profile/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/user_profile/build.gradle.kts b/feature/user_profile/build.gradle.kts new file mode 100644 index 0000000000..e8fe962eba --- /dev/null +++ b/feature/user_profile/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + alias(libs.plugins.mifos.android.feature) + alias(libs.plugins.mifos.android.library.compose) + alias(libs.plugins.kotlin.parcelize) +} + + +android { + namespace = "org.mifos.mobile.feature.third.party.user_profile" +} + +dependencies { + implementation(projects.ui) + implementation(projects.core.common) + implementation(projects.core.model) + implementation(projects.core.data) + + implementation(libs.squareup.retrofit2) { + // exclude Retrofit’s OkHttp peer-dependency module and define your own module import + exclude(module = "okhttp") + } + implementation(libs.squareup.retrofit.adapter.rxjava) + implementation(libs.squareup.retrofit.converter.gson) + implementation(libs.squareup.okhttp) + implementation(libs.squareup.logging.interceptor) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} \ No newline at end of file diff --git a/feature/user_profile/consumer-rules.pro b/feature/user_profile/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/feature/user_profile/proguard-rules.pro b/feature/user_profile/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/feature/user_profile/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/user_profile/src/androidTest/java/org/mifos/mobile/feature/user_profile/ExampleInstrumentedTest.kt b/feature/user_profile/src/androidTest/java/org/mifos/mobile/feature/user_profile/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..91e7a08a89 --- /dev/null +++ b/feature/user_profile/src/androidTest/java/org/mifos/mobile/feature/user_profile/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package org.mifos.mobile.feature.user_profile + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("org.mifos.mobile.feature.user_profile.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/user_profile/src/main/AndroidManifest.xml b/feature/user_profile/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a5918e68ab --- /dev/null +++ b/feature/user_profile/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileDetails.kt b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/screens/UserProfileDetails.kt similarity index 86% rename from app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileDetails.kt rename to feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/screens/UserProfileDetails.kt index 3e495cc199..9acd1d1d61 100644 --- a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserProfileDetails.kt +++ b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/screens/UserProfileDetails.kt @@ -1,6 +1,5 @@ -package org.mifos.mobile.ui.user_profile +package org.mifos.mobile.feature.user_profile.screens -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding @@ -10,14 +9,14 @@ import androidx.compose.material3.Text 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.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import org.mifos.mobile.R +import org.mifos.mobile.feature.third.party.user_profile.R +import org.mifos.mobile.feature.user_profile.utils.UserDetails /** * @author pratyush @@ -45,9 +44,9 @@ fun UserProfileDetails( tint = MaterialTheme.colorScheme.surfaceTint, contentDescription = null ) - userDetails.phoneNumber?.let { + if( userDetails.phoneNumber != null ){ Text( - text = it, + text = userDetails.phoneNumber, color = MaterialTheme.colorScheme.onSurface, style = TextStyle(fontSize = 14.sp) ) @@ -63,9 +62,9 @@ fun UserProfileDetails( tint = MaterialTheme.colorScheme.surfaceTint, contentDescription = null ) - userDetails.dob?.let { + if( userDetails.dob != null ){ Text( - text = it, + text = userDetails.dob, color = MaterialTheme.colorScheme.onSurface, style = TextStyle(fontSize = 14.sp) ) @@ -81,9 +80,9 @@ fun UserProfileDetails( tint = MaterialTheme.colorScheme.surfaceTint, contentDescription = null ) - userDetails.gender?.let { + if(userDetails.gender != null) { Text( - text = it, + text = userDetails.gender, color = MaterialTheme.colorScheme.onSurface, style = TextStyle(fontSize = 14.sp) ) diff --git a/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/screens/UserProfileScreen.kt b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/screens/UserProfileScreen.kt new file mode 100644 index 0000000000..a65a6932a6 --- /dev/null +++ b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/screens/UserProfileScreen.kt @@ -0,0 +1,324 @@ +package org.mifos.mobile.feature.user_profile.screens + +import android.content.Context +import android.content.res.Configuration +import android.graphics.Bitmap +import android.graphics.BitmapFactory +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Divider +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +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.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.toBitmap +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import org.mifos.mobile.core.common.Network +import org.mifos.mobile.core.common.utils.DateHelper +import org.mifos.mobile.core.datastore.PreferencesHelper +import org.mifos.mobile.core.model.entity.client.Client +import org.mifos.mobile.core.model.entity.client.Group +import org.mifos.mobile.core.ui.component.MFScaffold +import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay +import org.mifos.mobile.core.ui.component.MifosUserImage +import org.mifos.mobile.core.ui.component.NoInternet +import org.mifos.mobile.core.ui.component.UserProfileField +import org.mifos.mobile.core.ui.component.UserProfileTopBar +import org.mifos.mobile.feature.third.party.user_profile.R +import org.mifos.mobile.feature.user_profile.utils.TextDrawable +import org.mifos.mobile.feature.user_profile.utils.UserDetailState +import org.mifos.mobile.feature.user_profile.utils.UserDetails +import org.mifos.mobile.feature.user_profile.viewmodel.UserDetailViewModel + +/** + * @author pratyush + * @since 20/12/2023 + */ + +@Composable +fun UserProfileScreen( + viewModel: UserDetailViewModel = hiltViewModel(), + navigateBack: () -> Unit, + changePassword: () -> Unit +) { + + val context = LocalContext.current + val uiState by viewModel.userDetailUiState.collectAsStateWithLifecycle() + var isOnline by remember { mutableStateOf(false) } + + isOnline = Network.isConnected(context) + + UserProfileScreen( + navigateBack = navigateBack, + uiState = uiState, + changePassword = changePassword, + isOnline = isOnline + ) +} + +@Composable +fun UserProfileScreen( + navigateBack: () -> Unit, + uiState: UserDetailState, + changePassword: () -> Unit, + isOnline: Boolean +) { + val context = LocalContext.current + val snackBarHostState = remember { + SnackbarHostState() + } + var userBitmap : Bitmap? = getUserImage( null, context)!! + var client : Client = Client() + + MFScaffold( + topBarTitleResId = R.string.user_details, + navigateBack = navigateBack, + snackbarHost = { SnackbarHost(hostState = snackBarHostState) }, + scaffoldContent = { paddingValues -> + + Box( + Modifier + .fillMaxSize() + ) { + Column( + modifier = Modifier + .fillMaxSize(), + ) { + + when (uiState) { + is UserDetailState.ShowError -> { + LaunchedEffect(true) { + snackBarHostState.showSnackbar( + message = context.getString(uiState.message), + actionLabel = "Ok", + duration = SnackbarDuration.Short + ) + } + } + + is UserDetailState.Loading -> { + MifosProgressIndicatorOverlay() + } + + is UserDetailState.ShowUserDetails -> { + client = uiState.client + UserProfileContent( + changePassword = changePassword, + client = uiState.client, + isOnline = isOnline, + bitmap = userBitmap!!, + home = navigateBack + ) + + } + + is UserDetailState.ShowUserImage -> { + userBitmap = getUserImage(uiState.image, context) + userBitmap.let { + UserProfileContent( + changePassword = changePassword, + client = client, + isOnline = isOnline, + bitmap = it!!, + home = navigateBack + ) + } + } + } + } + } + } + ) +} + +@Composable +fun UserProfileContent( + changePassword: () -> Unit, + client : Client, + isOnline: Boolean, + bitmap: Bitmap, + home: () -> Unit +) { + val context = LocalContext.current + + val userName = nullFieldCheck(context.getString(R.string.username), client.displayName, context ) + val accountNumber = nullFieldCheck(context.getString(R.string.account_number), client.accountNo, context) + val activationDate = nullFieldCheck( + context.getString(R.string.activation_date), + DateHelper.getDateAsString(client.activationDate), + context + ) + val officeName = nullFieldCheck(context.getString(R.string.office_name), client.officeName, context) + val clientType = nullFieldCheck(context.getString(R.string.client_type), client.clientType?.name, context) + val groups = nullFieldCheck(context.getString(R.string.groups), getGroups(client.groups, context), context) + val clientClassification = client.clientClassification?.name ?: "-" + val phoneNumber = nullFieldCheck(context.getString(R.string.phone_number), client.mobileNo, context) + val dob = if (client.dobDate.size != 3) { + context.getString(R.string.no_dob_found) + } else { + DateHelper.getDateAsString(client.dobDate) + } + val gender = nullFieldCheck(context.getString(R.string.gender), client.gender?.name, context) + val userDetails = UserDetails( + userName = userName, + accountNumber = accountNumber, + activationDate = activationDate, + officeName = officeName, + clientType = clientType, + groups = groups, + clientClassification = clientClassification, + phoneNumber = phoneNumber, + dob = dob, + gender = gender + ) + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + UserProfileTopBar(home = home, text = R.string.user_details) + + if (!isOnline) { + NoInternet( + icon = R.drawable.ic_error_black_24dp, + error = R.string.error_fetching_user_profile + ) + return + } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 100.dp, bottom = 20.dp), + horizontalArrangement = Arrangement.Center + ) { + MifosUserImage( + bitmap = bitmap, + modifier = Modifier.size(100.dp) + ) + } + Divider(color = Color(0xFF8E9099)) + userDetails.userName?.let { UserProfileField(label = R.string.username, value = it) } + userDetails.accountNumber?.let { + UserProfileField( + label = R.string.account_number, value = it + ) + } + userDetails.activationDate?.let { + UserProfileField( + label = R.string.activation_date, value = it + ) + } + userDetails.officeName?.let { UserProfileField(label = R.string.office_name, value = it) } + userDetails.groups?.let { UserProfileField(label = R.string.groups, value = it) } + userDetails.clientType?.let { UserProfileField(label = R.string.client_type, value = it) } + userDetails.clientClassification?.let { + UserProfileField( + label = R.string.client_classification, value = it + ) + } + UserProfileField(text = R.string.change_password, + icon = R.drawable.ic_keyboard_arrow_right_black_24dp, + onClick = { changePassword.invoke() }) + + UserProfileDetails(userDetails = userDetails) + } +} + +private fun nullFieldCheck(field: String, value: String?, context : Context): String { + return value + ?: (context.getString(R.string.no) + context.getString(R.string.blank) + field + + context.getString(R.string.blank) + context.getString(R.string.found)) +} + +private fun getGroups(groups: List?, context: Context): String { + if (groups?.isEmpty() == true) { + return context.getString( + R.string.not_assigned_with_any_group, + ) // no groups entry in database for the + // client + } + val builder = StringBuilder() + if (groups != null) { + for ((_, _, name) in groups) { + builder.append(context.getString(R.string.string_and_string, name, " | ")) + } + } + return builder.toString().substring(0, builder.toString().length - 2) +} + +class UserProfileScreenPreviewProvider : PreviewParameterProvider { + val sampleBitmap = BitmapFactory.decodeResource( null, R.drawable.mifos_logo) + override val values: Sequence + get() = sequenceOf( + UserDetailState.Loading, + UserDetailState.ShowError(0), + UserDetailState.ShowUserDetails(Client()), + UserDetailState.ShowUserImage(sampleBitmap), + ) +} + +@Composable +private fun getUserImage( bitmap: Bitmap?, context: Context,): Bitmap { + val preferencesHelper = PreferencesHelper(context) + var userBitmap = bitmap + + if (userBitmap == null) { + val textDrawable = TextDrawable.builder() + .beginConfig() + .width(100) + .height(100) + .toUpperCase() + .endConfig() + .buildRound( + ( + if (preferencesHelper.clientName.isNullOrEmpty()) { + preferencesHelper.userName + } else { + preferencesHelper.clientName + } + ) + ?.substring(0, 1), + ContextCompat.getColor(context, R.color.primary), + ) + userBitmap = textDrawable.toBitmap() + } + + return userBitmap +} + +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO) +@Composable +fun UserProfileScreenPreview( + @PreviewParameter(UserProfileScreenPreviewProvider::class) userDetailState: UserDetailState +) { + UserProfileScreen( + {}, + userDetailState, + {}, + true + ) +} \ No newline at end of file diff --git a/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/ImageUtil.kt b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/ImageUtil.kt new file mode 100644 index 0000000000..111f01d16d --- /dev/null +++ b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/ImageUtil.kt @@ -0,0 +1,136 @@ +package org.mifos.mobile.feature.user_profile.utils + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Matrix +import android.graphics.Paint +import android.util.Log + +/** + * Created by dilpreet on 10/8/17. + */ +class ImageUtil { + // Default width and height + fun compressImage(decodedBytes: ByteArray): Bitmap? { + return compress(decodedBytes, 816.0f, 612.0f) + } + + fun compressImage(decodedBytes: ByteArray, maxHeight: Float, maxWidth: Float): Bitmap? { + return compress(decodedBytes, maxHeight, maxWidth) + } + + private fun compress(decodedBytes: ByteArray, maxHeight: Float, maxWidth: Float): Bitmap? { + var scaledBitmap: Bitmap? = null + val options = BitmapFactory.Options() + +// by setting this field as true, the actual bitmap pixels are not loaded in the memory. +// Just the bounds are loaded. If +// you try the use the bitmap here, you will get null. + options.inJustDecodeBounds = true + var bmp = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options) + var actualHeight = options.outHeight + var actualWidth = options.outWidth + var imgRatio = actualWidth / actualHeight.toFloat() + val maxRatio = maxWidth / maxHeight + +// width and height values are set maintaining the aspect ratio of the image + if (actualHeight > maxHeight || actualWidth > maxWidth) { + if (imgRatio < maxRatio) { + imgRatio = maxHeight / actualHeight + actualWidth = (imgRatio * actualWidth).toInt() + actualHeight = maxHeight.toInt() + } else if (imgRatio > maxRatio) { + imgRatio = maxWidth / actualWidth + actualHeight = (imgRatio * actualHeight).toInt() + actualWidth = maxWidth.toInt() + } else { + actualHeight = maxHeight.toInt() + actualWidth = maxWidth.toInt() + } + } + +// setting inSampleSize value allows to load a scaled down version of the original image + options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight) + +// inJustDecodeBounds set to false to load the actual bitmap + options.inJustDecodeBounds = false + +// this options allow android to claim the bitmap memory if it runs low on memory + options.inPurgeable = true + options.inInputShareable = true + options.inTempStorage = ByteArray(16 * 1024) + try { + bmp = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.size, options) + } catch (exception: OutOfMemoryError) { + Log.e(ImageUtil::class.java.name, exception.toString()) + } + try { + scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888) + } catch (exception: OutOfMemoryError) { + Log.e(ImageUtil::class.java.name, exception.toString()) + } + val ratioX = actualWidth / options.outWidth.toFloat() + val ratioY = actualHeight / options.outHeight.toFloat() + val middleX = actualWidth / 2.0f + val middleY = actualHeight / 2.0f + val scaleMatrix = Matrix() + scaleMatrix.setScale(ratioX, ratioY, middleX, middleY) + val canvas = Canvas(scaledBitmap!!) + canvas.setMatrix(scaleMatrix) + canvas.drawBitmap( + bmp, + middleX - bmp.width / 2, + middleY - bmp.height / 2, + Paint(Paint.FILTER_BITMAP_FLAG), + ) + scaledBitmap = Bitmap.createBitmap( + scaledBitmap, + 0, + 0, + scaledBitmap.width, + scaledBitmap.height, + null, + true, + ) + return scaledBitmap + } + + private fun calculateInSampleSize( + options: BitmapFactory.Options, + reqWidth: Int, + reqHeight: Int, + ): Int { + val height = options.outHeight + val width = options.outWidth + var inSampleSize = 1 + if (height > reqHeight || width > reqWidth) { + val heightRatio = Math.round(height.toFloat() / reqHeight.toFloat()) + val widthRatio = Math.round(width.toFloat() / reqWidth.toFloat()) + inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio + } + val totalPixels = width * height.toFloat() + val totalReqPixelsCap = reqWidth * reqHeight * 2.toFloat() + while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) { + inSampleSize++ + } + return inSampleSize + } + + companion object { + /** + * Reference : https://developer.android.com/topic/performance/graphics/load-bitmap.html + * And for scaling : + * https://stackoverflow.com/questions/8722359/scale-rotate-bitmap-using-matrix-in-android/8722592#8722592 + */ + @JvmStatic + var instance: ImageUtil? = null + get() { + if (field == null) { + field = ImageUtil() + } + return field + } + private set + } +} diff --git a/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/TextDrawable.kt b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/TextDrawable.kt new file mode 100644 index 0000000000..822e3b5127 --- /dev/null +++ b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/TextDrawable.kt @@ -0,0 +1,281 @@ +package org.mifos.mobile.feature.user_profile.utils + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.ColorFilter +import android.graphics.Paint +import android.graphics.PixelFormat +import android.graphics.RectF +import android.graphics.Typeface +import android.graphics.drawable.ShapeDrawable +import android.graphics.drawable.shapes.OvalShape +import android.graphics.drawable.shapes.RectShape +import android.graphics.drawable.shapes.RoundRectShape +import java.util.Locale + +class TextDrawable private constructor(builder: Builder) : ShapeDrawable(builder.shape) { + private val textPaint: Paint + private val borderPaint: Paint + private val text: String + private val color: Int + private val shape: RectShape + private val height: Int + private val width: Int + private val fontSize: Int + private val radius: Float + private val borderThickness: Int + private fun getDarkerShade(color: Int): Int { + return Color.rgb( + (SHADE_FACTOR * Color.red(color)).toInt(), + (SHADE_FACTOR * Color.green(color)).toInt(), + (SHADE_FACTOR * Color.blue(color)).toInt(), + ) + } + + override fun draw(canvas: Canvas) { + super.draw(canvas) + val r = bounds + + // draw border + if (borderThickness > 0) { + drawBorder(canvas) + } + val count = canvas.save() + canvas.translate(r.left.toFloat(), r.top.toFloat()) + + // draw text + val width = if (width < 0) r.width() else width + val height = if (height < 0) r.height() else height + val fontSize = if (fontSize < 0) Math.min(width, height) / 2 else fontSize + textPaint.textSize = fontSize.toFloat() + canvas.drawText( + text, + width / 2.toFloat(), + height / 2 - (textPaint.descent() + textPaint.ascent()) / 2, + textPaint, + ) + canvas.restoreToCount(count) + } + + private fun drawBorder(canvas: Canvas) { + val rect = RectF(bounds) + rect.inset(borderThickness / 2.toFloat(), borderThickness / 2.toFloat()) + if (shape is OvalShape) { + canvas.drawOval(rect, borderPaint) + } else if (shape is RoundRectShape) { + canvas.drawRoundRect(rect, radius, radius, borderPaint) + } else { + canvas.drawRect(rect, borderPaint) + } + } + + override fun setAlpha(alpha: Int) { + textPaint.alpha = alpha + } + + override fun setColorFilter(cf: ColorFilter?) { + textPaint.colorFilter = cf + } + + override fun getOpacity(): Int { + return PixelFormat.TRANSLUCENT + } + + override fun getIntrinsicWidth(): Int { + return width + } + + override fun getIntrinsicHeight(): Int { + return height + } + + class Builder : IConfigBuilder, IShapeBuilder, IBuilder { + var text = "" + var color: Int + var borderThickness: Int + var iconWidth: Int + var iconHeight: Int + var font: Typeface + var shape: RectShape + var iconTextColor: Int + var iconFontSize: Int + var isBold: Boolean + var iconToUpperCase: Boolean + var radius = 0f + override fun width(width: Int): IConfigBuilder { + iconWidth = width + return this + } + + override fun height(height: Int): IConfigBuilder { + iconHeight = height + return this + } + + override fun textColor(color: Int): IConfigBuilder { + iconTextColor = color + return this + } + + override fun withBorder(thickness: Int): IConfigBuilder { + borderThickness = thickness + return this + } + + override fun useFont(font: Typeface): IConfigBuilder { + this.font = font + return this + } + + override fun fontSize(size: Int): IConfigBuilder { + iconFontSize = size + return this + } + + override fun bold(): IConfigBuilder { + isBold = true + return this + } + + override fun toUpperCase(): IConfigBuilder { + iconToUpperCase = true + return this + } + + override fun beginConfig(): IConfigBuilder { + return this + } + + override fun endConfig(): IShapeBuilder { + return this + } + + override fun rect(): IBuilder { + shape = RectShape() + return this + } + + override fun round(): IBuilder { + shape = OvalShape() + return this + } + + override fun roundRect(radius: Int): IBuilder { + this.radius = radius.toFloat() + val radii = floatArrayOf( + radius.toFloat(), + radius.toFloat(), + radius.toFloat(), + radius.toFloat(), + radius.toFloat(), + radius.toFloat(), + radius.toFloat(), + radius.toFloat(), + ) + shape = RoundRectShape(radii, null, null) + return this + } + + override fun buildRect(text: String, color: Int): TextDrawable { + rect() + return build(text, color) + } + + override fun buildRoundRect(text: String, color: Int, radius: Int): TextDrawable { + roundRect(radius) + return build(text, color) + } + + override fun buildRound(text: String?, color: Int): TextDrawable { + round() + return build(text!!, color) + } + + override fun build(text: String, color: Int): TextDrawable { + this.color = color + this.text = text + return TextDrawable(this) + } + + init { + color = Color.GRAY + iconTextColor = Color.WHITE + borderThickness = 0 + iconWidth = -1 + iconHeight = -1 + shape = RectShape() + font = Typeface.create("sans-serif-light", Typeface.NORMAL) + iconFontSize = -1 + isBold = false + iconToUpperCase = false + } + } + + interface IConfigBuilder { + fun width(width: Int): IConfigBuilder + fun height(height: Int): IConfigBuilder + fun textColor(color: Int): IConfigBuilder + fun withBorder(thickness: Int): IConfigBuilder + fun useFont(font: Typeface): IConfigBuilder + fun fontSize(size: Int): IConfigBuilder + fun bold(): IConfigBuilder + fun toUpperCase(): IConfigBuilder + fun endConfig(): IShapeBuilder + } + + interface IBuilder { + fun build(text: String, color: Int): TextDrawable + } + + interface IShapeBuilder { + fun beginConfig(): IConfigBuilder + fun rect(): IBuilder + fun round(): IBuilder + fun roundRect(radius: Int): IBuilder + fun buildRect(text: String, color: Int): TextDrawable + fun buildRoundRect(text: String, color: Int, radius: Int): TextDrawable + fun buildRound(text: String?, color: Int): TextDrawable + } + + companion object { + private const val SHADE_FACTOR = 0.9f + fun builder(): IShapeBuilder { + return Builder() + } + } + + init { + + // shape properties + shape = builder.shape + height = builder.iconHeight + width = builder.iconWidth + radius = builder.radius + + // text and color + text = if (builder.iconToUpperCase) builder.text.uppercase(Locale.ROOT) else builder.text + color = builder.color + + // text paint settings + fontSize = builder.iconFontSize + textPaint = Paint() + textPaint.color = builder.iconTextColor + textPaint.isAntiAlias = true + textPaint.isFakeBoldText = builder.isBold + textPaint.style = Paint.Style.FILL + textPaint.typeface = builder.font + textPaint.textAlign = Paint.Align.CENTER + textPaint.strokeWidth = builder.borderThickness.toFloat() + + // border paint settings + borderThickness = builder.borderThickness + borderPaint = Paint() + borderPaint.color = getDarkerShade(color) + borderPaint.style = Paint.Style.STROKE + borderPaint.strokeWidth = borderThickness.toFloat() + + // drawable paint color + val paint = paint + paint.color = color + } +} diff --git a/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/UserDetailUiState.kt b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/UserDetailUiState.kt new file mode 100644 index 0000000000..015c24f9b3 --- /dev/null +++ b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/UserDetailUiState.kt @@ -0,0 +1,11 @@ +package org.mifos.mobile.feature.user_profile.utils + +import android.graphics.Bitmap +import org.mifos.mobile.core.model.entity.client.Client + +sealed class UserDetailState { + object Loading : UserDetailState() + data class ShowError(val message: Int) : UserDetailState() + data class ShowUserDetails(val client: Client) : UserDetailState() + data class ShowUserImage(val image: Bitmap?) : UserDetailState() +} diff --git a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserDetails.kt b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/UserDetails.kt similarity index 92% rename from app/src/main/java/org/mifos/mobile/ui/user_profile/UserDetails.kt rename to feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/UserDetails.kt index 776a1b8c1c..337e5761d9 100644 --- a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserDetails.kt +++ b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/utils/UserDetails.kt @@ -1,4 +1,4 @@ -package org.mifos.mobile.ui.user_profile +package org.mifos.mobile.feature.user_profile.utils data class UserDetails( val userName: String?, diff --git a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserDetailViewModel.kt b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/viewmodel/UserDetailViewModel.kt similarity index 78% rename from app/src/main/java/org/mifos/mobile/ui/user_profile/UserDetailViewModel.kt rename to feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/viewmodel/UserDetailViewModel.kt index 114a581dde..c9f87f2aca 100644 --- a/app/src/main/java/org/mifos/mobile/ui/user_profile/UserDetailViewModel.kt +++ b/feature/user_profile/src/main/java/org/mifos/mobile/feature/user_profile/viewmodel/UserDetailViewModel.kt @@ -1,7 +1,11 @@ -package org.mifos.mobile.ui.user_profile +package org.mifos.mobile.feature.user_profile.viewmodel +import android.net.http.HttpException +import android.os.Build +import android.os.Bundle import android.util.Base64 import android.util.Log +import androidx.annotation.RequiresExtension import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -9,14 +13,14 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch -import org.mifos.mobile.R import org.mifos.mobile.core.data.repositories.HomeRepository import org.mifos.mobile.core.data.repositories.UserDetailRepository import org.mifos.mobile.core.datastore.PreferencesHelper +import org.mifos.mobile.core.model.entity.client.Client import org.mifos.mobile.core.model.entity.notification.NotificationRegisterPayload -import org.mifos.mobile.utils.ImageUtil -import org.mifos.mobile.utils.UserDetailUiState -import retrofit2.HttpException +import org.mifos.mobile.feature.third.party.user_profile.R +import org.mifos.mobile.feature.user_profile.utils.ImageUtil +import org.mifos.mobile.feature.user_profile.utils.UserDetailState import javax.inject.Inject @HiltViewModel @@ -28,18 +32,18 @@ class UserDetailViewModel @Inject constructor( @Inject lateinit var preferencesHelper: PreferencesHelper - private val _userDetailUiState = MutableStateFlow(UserDetailUiState.Loading) - val userDetailUiState: StateFlow = _userDetailUiState + private val _userDetailUiState = MutableStateFlow(UserDetailState.Loading) + val userDetailUiState: StateFlow = _userDetailUiState val userDetails: Unit get() { viewModelScope.launch { homeRepositoryImp.currentClient().catch { _userDetailUiState.value = - UserDetailUiState.ShowError(R.string.error_fetching_client) + UserDetailState.ShowError(R.string.error_fetching_client) }.collect { client -> preferencesHelper.officeName = client.officeName - _userDetailUiState.value = UserDetailUiState.ShowUserDetails(client) + _userDetailUiState.value = UserDetailState.ShowUserDetails(client) } } } @@ -49,7 +53,7 @@ class UserDetailViewModel @Inject constructor( viewModelScope.launch { setUserProfile(preferencesHelper.userProfileImage) homeRepositoryImp.clientImage().catch { - _userDetailUiState.value = UserDetailUiState.ShowUserImage(null) + _userDetailUiState.value = UserDetailState.ShowUserImage(null) }.collect { val encodedString = it.string() val pureBase64Encoded = @@ -66,14 +70,15 @@ class UserDetailViewModel @Inject constructor( } val decodedBytes = Base64.decode(image, Base64.DEFAULT) val decodedBitmap = ImageUtil.instance?.compressImage(decodedBytes) - _userDetailUiState.value = UserDetailUiState.ShowUserImage(decodedBitmap) + _userDetailUiState.value = UserDetailState.ShowUserImage(decodedBitmap) } + @RequiresExtension(extension = Build.VERSION_CODES.S, version = 7) fun registerNotification(token: String) { viewModelScope.launch { val payload = preferencesHelper.clientId?.let { NotificationRegisterPayload(it, token) } userDetailRepositoryImp.registerNotification(payload).catch { e -> - if (e is HttpException && e.code() == 500) { + if (e is retrofit2.HttpException && e.code() == 500) { payload?.let { getUserNotificationId(it, token) } } }.collect { diff --git a/feature/user_profile/src/main/res/drawable/ic_cake_24dp.xml b/feature/user_profile/src/main/res/drawable/ic_cake_24dp.xml new file mode 100644 index 0000000000..1b1025e7ac --- /dev/null +++ b/feature/user_profile/src/main/res/drawable/ic_cake_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/user_profile/src/main/res/drawable/ic_error_black_24dp.xml b/feature/user_profile/src/main/res/drawable/ic_error_black_24dp.xml new file mode 100644 index 0000000000..b666c18189 --- /dev/null +++ b/feature/user_profile/src/main/res/drawable/ic_error_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/user_profile/src/main/res/drawable/ic_gender_24dp.xml b/feature/user_profile/src/main/res/drawable/ic_gender_24dp.xml new file mode 100644 index 0000000000..fb8596be49 --- /dev/null +++ b/feature/user_profile/src/main/res/drawable/ic_gender_24dp.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + diff --git a/feature/user_profile/src/main/res/drawable/ic_keyboard_arrow_right_black_24dp.xml b/feature/user_profile/src/main/res/drawable/ic_keyboard_arrow_right_black_24dp.xml new file mode 100644 index 0000000000..138fcc3ebe --- /dev/null +++ b/feature/user_profile/src/main/res/drawable/ic_keyboard_arrow_right_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/user_profile/src/main/res/drawable/ic_phone_24dp.xml b/feature/user_profile/src/main/res/drawable/ic_phone_24dp.xml new file mode 100644 index 0000000000..b9a9fdf2b7 --- /dev/null +++ b/feature/user_profile/src/main/res/drawable/ic_phone_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/feature/user_profile/src/main/res/drawable/mifos_logo.png b/feature/user_profile/src/main/res/drawable/mifos_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..39bef3caf150b0a43215c933e3798a3d1fa6d60e GIT binary patch literal 60004 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

    ^E8+GaYYC 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 + + #ffffffff + #000000 + #eaeaea + #ff14c416 + #ff8bf98a + #fff9ac06 + #FF87DBF9 + #fff9393c + #ffd1d1d1 + #8ad3da44 + #8ada6134 + #ff003fff + #FF0000 + #1C1C1C + + #B2C1C8 + + + #ff33b5e5 + + #33999999 + + #BB666666 + + #ff99cc00 + + #ffff4444 + + #ff0099cc + + #ff669900 + + #ffcc0000 + + #ffaa66cc + + #ffffbb33 + + #ffff8800 + + #ff00ddff + + #33CCCCCC + + + + + + + + #FF325ca8 + #0288D1 + #B3E5FC + #FF4081 + #212121 + #757575 + #FFFFFF + #BDBDBD + #ffffff + @color/blue_light + #EEEEEE + #00000000 + + + + @color/blue_light + @color/green_light + @color/red_light + @color/orange_light + + + \ No newline at end of file diff --git a/feature/user_profile/src/main/res/values/strings.xml b/feature/user_profile/src/main/res/values/strings.xml new file mode 100644 index 0000000000..6fe11674e3 --- /dev/null +++ b/feature/user_profile/src/main/res/values/strings.xml @@ -0,0 +1,23 @@ + + + + User Details + Failed to fetch User Profile + Username + Account Number + Activation Date + Office Name + Groups + Client Type + Client Classification + Change Password + Phone Number + No DOB found. + Gender + No + foundx +   + Not Assigned with any group + %1$s %2$s + Failed to fetch Client + \ No newline at end of file diff --git a/feature/user_profile/src/test/java/org/mifos/mobile/feature/user_profile/ExampleUnitTest.kt b/feature/user_profile/src/test/java/org/mifos/mobile/feature/user_profile/ExampleUnitTest.kt new file mode 100644 index 0000000000..7cea5948e1 --- /dev/null +++ b/feature/user_profile/src/test/java/org/mifos/mobile/feature/user_profile/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package org.mifos.mobile.feature.user_profile + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5384d6fd25..4487b0fb1e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -52,6 +52,7 @@ playServicesCodeScanner = "16.1.0" googleAppCodeScanner = "17.2.0" cameraxVersion = "1.3.1" compose-material = "1.6.8" +googleMaps = "4.4.1" [libraries] androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityVersion" } @@ -112,6 +113,7 @@ androidx-camera-camera2 = { group = "androidx.camera", name = "camera-camera2", androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "cameraxVersion"} androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraxVersion"} androidx-camera-core = { group = "androidx.camera", name = "camera-core", version.ref = "cameraxVersion" } +google-map-compose = { group = "com.google.maps.android", name = "maps-compose", version.ref = "googleMaps" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } diff --git a/settings.gradle.kts b/settings.gradle.kts index d238646534..f700820bdf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,5 +30,19 @@ include(":core:data") include(":core:network") include(":core:datastore") include(":feature:loan") +include(":feature:login") include(":feature:beneficiary") include(":feature:registration") +include(":feature:savings") +include(":feature:qr") +include(":feature:transfer-process") +include(":feature:account") +include(":feature:recent_transaction") +include(":feature:client_charge") +include(":feature:third-party-transfer") +include(":feature:help") +include(":feature:notification") +include(":feature:location") +include(":feature:about") +include(":feature:settings") +include(":feature:update-password") diff --git a/ui/src/main/java/org/mifos/mobile/core/ui/component/EmptyDataView.kt b/ui/src/main/java/org/mifos/mobile/core/ui/component/EmptyDataView.kt index 67e5df7bba..e04cf4b9d7 100644 --- a/ui/src/main/java/org/mifos/mobile/core/ui/component/EmptyDataView.kt +++ b/ui/src/main/java/org/mifos/mobile/core/ui/component/EmptyDataView.kt @@ -12,8 +12,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -22,7 +24,7 @@ import androidx.compose.ui.unit.sp @Composable fun EmptyDataView( modifier: Modifier = Modifier.fillMaxSize(), - icon: Int, + icon: Int? = null, error: Int, errorString: String? = null, ) { @@ -35,7 +37,8 @@ fun EmptyDataView( modifier = Modifier .size(100.dp) .padding(bottom = 12.dp), - painter = painterResource(id = icon), + imageVector = if(icon != null) ImageVector.vectorResource(id = icon) + else MifosIcons.Error, contentDescription = null, tint = MaterialTheme.colorScheme.onSecondary ) diff --git a/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosIcons.kt b/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosIcons.kt index 7f37442dca..7785a68650 100644 --- a/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosIcons.kt +++ b/ui/src/main/java/org/mifos/mobile/core/ui/component/MifosIcons.kt @@ -3,6 +3,7 @@ package org.mifos.mobile.core.ui.component import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.Error import androidx.compose.material.icons.filled.FilterList import androidx.compose.material.icons.filled.FlashOff import androidx.compose.material.icons.filled.FlashOn @@ -17,4 +18,5 @@ object MifosIcons { val Add = Icons.Filled.Add val Search = Icons.Filled.Search val WifiOff = Icons.Filled.WifiOff + val Error = Icons.Filled.Error } \ No newline at end of file